>> Hello, today I'm going to be porting
a .NET Framework WPF to .NET Core 3.
Now, this video will be a little longer than normal for
me because I want to show the entire
porting process start to finish,
so I'm actually going to split it up.
What you're watching now will be
the first part of a five video series,
in which we do the end-to-end migration
of a WPF application to .NET Core.
We recently had a blog post at Channel nine video where one of
our .NET PMs [inaudible] ported a simple wind forms app to .NET Core.
So this is going to be similar to that.
Some of the techniques will be the same.
But I wanted to do this video
also because my splitting
it up it's spending an hour or two audit,
I think we can see a more complex example.
This way we get to see a WPF app
as opposed to the wind forms, one [inaudible] ported.
So the migration process that we're going to go
through during these videos is four steps.
The first step which we're covering in this video
is all about getting ready to port.
It's about understanding the dependencies the project has so
that we know what work we have ahead of us,
and getting the existing .NET Framework project into
a state that will make it easy to port in the subsequent steps.
The second step is actually migrating the project file
itself. .NET Core projects use
the new SDK style CSPROJ file format,
so you need to either create
a new project file for your .NET Core port,
or you need to update
the existing CSPROJ file to use the SDK style.
The third step is what people
often think of when they think about porting to .NET
Core which is making the code level fixes
to get it building against the .NET Core target.
So that would be API level differences that need
addressed either because of changes in .NET Framework,
or in third-party packages you use.
Because we're using WCF,
here we're going to have to regenerate our WCF client and so on.
So it's all about those code level fix up.
This will actually be two videos.
This will be videos three and four
since it's the lion's share of the work.
Then the final step is running and testing the application.
People sometimes think that once it builds you're done,
but there are a number of differences
between .NET Framework and .NET Core
that don't show up until runtime.
So you really do need to make sure that you've tested the app,
you've exercised it's interesting code paths
on .NET Core and makes sure that it runs as well.
So that will be our fifth and final video.
So the app that we're porting like I
said it's a step up from Hello World but still fairly simple.
It's not especially large.
But I wanted to make it interesting by using
some dependencies that might be nontrivial to port,
and that are common in real-world WPF applications.
So this app is a commodity trading application.
I call it Bean Trader or you trade
imaginary beans with other users.
It communicates with a back end service using WCF,
it has a duplex NetTcp binding.
So it's got that WCF dependency.
It also uses some third-party UI controls from MahApps.Metro,
uses dependency injection on Castle.Windsor and whole variety
of .NET APIs that are common in WPF apps,
like getting settings from an app config file,
or the registry using a variety of resources, and so on.
The app is up on GitHub,
so you can go check it out.
If you want at github.com/mjrousos/BeanTrader.
You will find that WPF app in the Bean Trader client folder.
Bean Trader server has that back-end WCF service
which you can just run locally.
I have it hosted up in Azure right now.
So you can check it out for yourself.
I've got some notes on how the migration should be done.
Actually if you take a look at the branches,
there's Net Core branch which has
the final state already ported to .NET Core and .NET Framework,
so you can find the final part of product in the .NET Core branch.
Net effects, that master right now are
the same day of the initial state of the project,
and Migration process is a branch or I'm going
to commit after each of the videos I record,
so that you can see the state of the project
as it progresses through the migration process.
Also I do want to mention that this WPF app is not meant to be
used as a WPS best-practice sample or anything.
It's not a reference app for WPF.
That's both because I'm not a WPF expert,
and because I intentionally did some things in
roundabout ways in this application
to make the porting a little bit more interesting.
This is all about
the migration process from .NET Framework to .NET Core,
not about showcasing the right ways to do things and WPF.
Just a quick ordering.
So let me briefly demo
this app for you and then we'll go ahead and port it.
I'm not going to spend a long time on the demo
because the app itself is not the interesting part,
it's the way we make it work on core.
But if we run it, you see we've got
this Bean Trader UI it's styled
with MahApps themes that accents.
I can log in and it shows me how many beans I have,
and it shows me traits that people have proposed.
I will also start up a second instance of
this app so that we can see how in real time things are updated.
Let's log in here as a different user maybe as Daniel,
and then here I can see okay,
Daniel will give me 20 red beans for 10 blue and five green.
If I accept it, you can see that both users are notified,
bean counts are updated, and so on.
Now I've got a lot of blue beans,
so maybe I want to give five blue beans and get five green beans.
Then that shows up for both users as well.
So that's the application.
Nothing fancy, but it's hopefully a little bit more
real-world than just saying
hello on the screen because we're using a WCF client,
we're using app configuration,
we're using MahApps Metro dialogues, and so on.
So let's go ahead and port this.
The first step like I said is that we want to
prepare and understand the work that we have ahead of us.
So we're going to look at our dependencies.
We're going to look at third party dependencies
or we look at .NET dependencies.
For the third party wants the first step,
if you'll have packages.config file,
we're going to want to change this because
the packages.config file lists
all of the NuGet packages that our project depends on.
In fact, it lists the transitive closure
of all of the NuGet packages.
In .NET Core using the new SDK Sale project,
we no longer use packages.config.
Instead, we use the newer package reference format
to pull a NuGet dependencies.
So one of the things you're going to want to
do to get your project ready to port,
so you're going to want to make that transition.
So right click on "Packages.config",
and click "Migrate packages.config" to package reference,
if you haven't done this already with your project.
This is going to do two things for us.
First, it's going to migrate to
the new package reference format so that we can just
copy and paste our NuGet references
from the old project into the new one,
and it'll make that process super easy in the next video,
and it allows us to only reference top-level projects
instead of the whole transitive dependency tree.
So the migration tool in Visual Studio is really nice.
It identifies the top-level projects.
Then we have an opportunity to select
any other NuGet packages
that are dependent on that we'd like to
promote to top-level dependencies.
In this case, I don't want to make any of those top-level.
Then when we click "Okay", it makes the changes.
It's going to remove our packages.Config file,
and it will update the CSPROJ file
to use package reference instead.
So we'll let this finish generates
a little report showing what changed, that's fine.
Let's go ahead and we'll save the changes to the project file.
Then I'm actually going
to open this up in Visual Studio code so that we can easily
see the CSPROJ file without having
to unload the project in Visual Studio.
You can see that down at the bottom of the project file there's
a new item group with these package reference elements.
So this is the replacement for packages.config.
Like I said, one of the nice things about
this format is that
now I'm just looking at the top-level packages.
So these are the packages that I need to have
present for my project to work,
and that are going to need to work on .NET Core.
So at this point, I can review these and make sure
that I have a version that's going to
work for me when I actually do the .NET Core migration.
So if I go out to NuGet.org,
we can start looking at some of these up,
and see what our options are.
So we have Castle Windsor for example,
and we're using Castle Windsor version 4.1.1.
So I'll click on that version,
and you can see it dependencies that it has .NET Framework
dependencies and .NET Standard 1.6 dependencies.
So this package should work as
is on .NET Core because .NET Core of course,
can depend .NET Standard packages as well.
Another view you can use instead of NuGet.org if it's hard
for you to tell which .NET targets are supported,
you've got a Phuket.org.
It's the exact same URL but replace
the N in NuGet with an F. This is
a site that some community members made
that gives another view into NuGet packages.
It gives a little more details as far as what APIs are present,
and it's specifically lists frameworks up at
the top here so we can see .NET 4.5 and .NET Standard 1.6.
So this package is fine.
We can also look at say MahApps.Metro,
and we see that once it loads here,
MahApps Metro version two t least
supports .NET Framework and .NET Core 3.0,
but where are using version 1.6.5.
So if we go back to that, we see here it doesn't support .NET Core,
it always works out in a framework.
This can be similar if I look up for example the
[inaudible] or the Microsoft Azure common libraries.
All of these support .NET Core or .NET Standard
in a more recent version.
So here two-to-one supports .NET Standard one to four,
but some of the older versions that
my initial sample dependent on .NET supporter.
This is going to be common.
A lot of older WPF and
wind forms applications will be using older versions of
NuGet packages that don't support .NET Standard or .NET Core,
but they have newer versions that do.
So that's the second thing you can do to get ready to port.
You remember that I said,
you can get ready to port by migrating to
the package reference format for you NuGet packages.
The other thing you could do is upgrade to
newer versions so that you know when you go to .NET Core,
you'll be able to use the same NuGet packages
you were using previously.
Now, sometimes that may take a little bit of work,
like if we make a major version change
like from MahApps.Metro one to two,
there could be breaking changes.
So in some cases,
you may not want to actually update because you don't want to
make all of the changes to use this new version,
but eventually that should be done.
So this is a good time to take care of it if you're able
to because it's going to make the migration a little bit easier.
Now, what if you don't find
an updated version that references .NET Standard or .NET Core?
Or what if you really don't want to
move because you don't want to take
some change in surface area of newer versions of these packages?
>> It's possible to depend on .NET Framework libraries,
and NuGet packages, from .NET Core applications.
We allow that because the surface areas are so similar nowadays,
that those sorts of dependencies often work,
but they don't always work.
See, if you are going to depend on a .NET Framework package,
you need to be a little bit cautious about it.
You need to keep in mind that
that's going to require some extra testing,
because what will happen is we'll allow it to build,
but at runtime, when you're running on .NET Core,
if you .NET Framework, DLL,
or NuGet package that you're depending on,
calls an API that doesn't exist
on .NET Core that only exists on .NET Framework,
you're going to get a runtime error.
So there's an extra test burden if you depend
on older .NET framework packages, or libraries.
But we allow it because there are some situations
where an old NuGet package hasn't been updated in years,
it would work fine on .NET Core,
it just hasn't been retargeted.
So in those cases,
we allow you to use those libraries, they work.
Just make sure that you test that at runtime you're
not going to get a missing method exception,
or something like that.
So we can go through these individually,
but I know that they all either are supported on that .NET Core,
or have updated versions that are.
I'm not going to update to the newer versions now,
I'll do that in the next video.
In the interest of keeping this first one short.
But that's reviewing your third party dependencies.
The other thing we want to look at is
first part of .NET dependencies,
like different APIs we call it for the .NET Framework,
to make sure that those same APIs
exist in the .NET Core surface area.
For that, we're going to use the .NET
Core part of the .NET portability analyzer,
which is a tool that the .NET team created and maintains.
That will look at your binaries,
find all of the .NET APIs that are called,
and then produce a report showing whether those APIs are
available on different .NET targets that you care about.
The tool works both as a Visual Studio plugin,
or from the command line.
Now we've talked about this tool a lot in the past.
So if you're already familiar with it,
you can be done with this video and move on to the next one,
because the next five or six minutes will be review.
But for folks who haven't used the tool before,
let me show you quickly how it works.
Like I said, you can use it from Visual Studio,
or from the command line. Both work well.
I'm going to do it from the command line,
just because I feel like we demo that less often,
and I think it's a great option.
If you're on API port, you'll see
there's a few different commands you can run.
List targets will show all of the .NET
targets that you can get information on.
List output formats will show
the different output formats that we can use.
I'll write that quickly.
You can get output formatted as HTML, Excel, or JSON.
Then the command that we really care about is Analyze,
which is actually going to do the analysis of our projects.
Will do API port analyze, -F,
give the path to the binaries that we want to analyze,
-R to specify which output formats I wanted.
I like to do both HTML and Excel,
because I think they're both useful.
The HTML report in my opinion is the most human readable and the
easiest to get an overall sense
of what APIs are present are missing.
The Excel report is nice though because of course it's writeable.
You can put notes in a new column.
So if you're reviewing this with a team, or working through,
maybe migrating away from some APIs
that aren't available on .NET Core,
you can use the Excel spreadsheet to
track that keep notes and so on.
They'll do -T to specify the .NET target that we want to use.
I'll say .NET Core.
I couldn't specify which version of .NET Core,
but by default, all of the targets will pick the latest version.
In this case, that's .NET Core 3.0.
So I run the tool, it has produced this HTML file,
as well as the Excel spreadsheet you see.
So let's pop this open and see what's in here.
The top we have all of the binaries that were
found in that path that I provided,
and how compatible they are with .NET Core.
You can see these are fairly high numbers, which is nice.
But something I want to emphasize is that you
shouldn't look too much at this number.
It doesn't mean a whole lot.
You may have 85 or 90 percent here, and that's okay.
Because the APIs that you're depending on
are easily worked around,
or there are other alternatives that you can use instead.
You might have a 98 percent here, and have a big problem,
because you're using some APIs like creating app domains,
or remoting APIs, that simply aren't supported on .NET Core.
So now you have more work ahead of you.
This single number doesn't tell you a whole lot.
What you really need to look at is to drill into
the binaries that you own the source code for.
So in this case, that's only this top one BeanTraderClient.
Look at the APIs that are missing,
which would be the ones with these red
X's here in the .NET Core column.
Make sure that for all of those,
you have an idea of how to work around them.
In this case, the only APIs that are missing from
the BeanTraderClient app are some WCF client-based APIs,
close and open are missing.
I'm not super worried about these,
because I know that WCF client APIs
are generally supported on .NET core.
Not all WCF client APIs,
but a good subsection.
So certainly there are ways to open and close WCF clients.
There are probably alternative APIs I could use here,
probably async alternatives to
these synchronous ones that are no longer present.
So this makes me feel pretty good.
Now, we do have information on our other binaries,
but this is not interesting either,
because I don't own the source.
There's nothing I can do about these ones. Like Castle Windsor.
Castle Windsor is missing some system.web APIs that it was using.
If this was my code,
would have to migrate to using some ASP .NET Core alternatives,
and for the logical set data stuff I would use async locals,
but I don't own Castle Windsor.
In fact, remember we looked and we
saw that Castle Windsor version 4.1.1
Is targets .NET Framework and .NET Standard 16.
So because it targets .NET Standard 16,
I have confidence that there is
a Castle Windsor binary in
that NuGet package, that's going to work for me.
This particular one that was in
my binary output folder that targets .NET Framework,
well, maybe it doesn't work,
but the Castle Windsor package supports .NET Standard.
So there's another binary that will be used, that will work.
So this one doesn't even really matter.
So these other libraries,
these other binaries, you need to make
sure that there are versions of these that work.
But we've already done that by
looking through the NuGet package dependencies,
and other third party dependencies that our project has,
and making sure that there are
alternatives there that will work for .NET Core.
So when we look at the API port report,
you really only need to look at things that you own,
and just look at these APIs to make sure there's
nothing really crucial here that's going to block you.
So at this point, I feel pretty good about porting to .NET Core.
We've reviewed our NuGet package dependencies,
we've upgraded to use package reference format,
which will make the migration easier.
We've looked at an API portability report
to make sure that we understand
which .NET APIs we are depending
on may not be present once we migrate to .NET Core,
so that we know that those are things that we can work around.
At this point, we're ready to start the migration,
which we'll do in video two.