>> Welcome back to Migrating a .NET Framework WPF app to .NET Core 3. In this second video, we're going to actually
start the migration, now that we've done
the preparatory work, we're going to create
a new Csproj make sure that we have updated versions
of our NuGet packages. So that will be all
set up for a video three where we actually
start making code changes. So again, I see migration has a four-step process
where you prepare, you get the project
file setup and adjust NuGet packages and other
third-party dependency references. Then a fourth step where you fix the code and then you
run and you test and you find any runtime differences
and you account for those. So we're on step two now. Now, one of the first things
we have to think about when we're setting up
our new Csproject file is where does this project file live? That might not seem on the surface like a very interesting decision, but it actually is
because it's going to, no matter what answer we give to that question we're going
to have some challenges. Now, the first option
and the one that I would recommend starting
maybe six months from now or whatever .NET Core
3 is in a state that you can go live on it would be to just
replace the old project file. You could imagine getting rid of the old project file putting
a new one in its place, and if you want to target
both .NET Framework and .Net Core you could use
multitargeting to do that. Right now that's not a great option because multitargeting doesn't
work well with the designer. So, if you need to go in and use the WPF designer to shift
some UI elements or drop something in you're
not going to get to do that with this new project
or at least it may not always work well and
multitargeting allows it to work occasionally but you
can't count on it. So you'll just be doing your
UI updates in the XAML. The whole multitargeting
process I think is the way we're going to
want to do this eventually but I don't think that experience is there yet in the
.NET Core 3 previews. So if we don't do that yet, then we're going to have to project files assuming that we
want to continue building for .NET Framework since that's
the thing that we can go live on now and it has
the designer support. So we either can put
this C# project files next to each other in same directory or we put them in
separate directories. If we put them next to each
other in the same directory, that's going to make it easy
to reference all the source, the XAML and the resources because they're all going to be at
the same place as relatively. But that's going to
introduce a challenge because these two Csproj files are
going to have the same default, intermediate output and
the same bin folders. They're going to get in each other's way when they're restoring packages,
when they're building. So you're going to have
to make some changes to actually have them you
separate output folders. Unfortunately, there's actually
a couple of issues right into how with publishing a .NET Core app when you've changed
the intermediate output folder or at least a .NET Core WPF app, such that you do a .NET clean
prior to running .NET publish, totally something
you can work around. But there's just some
rough edges here, and it's going to be
a little bit of a hustle. Now again, this is a perfectly good option but we're going to have
to do a little bit of work to make it do what we want as far as just updating
these output paths. So you might think okay, well, I'll avoid that by putting
these in separate directories. So that's going to solve all the
problems I just talked about, but now you're going
to have a problem. The new .NET Core project
file format doesn't need to explicitly depend on
C-Sharp source, XAML, pages, or resurrects files that are in its directory or in directories
underneath it's directory. If you're in a separate folder with your Csproj you
don't get that benefit, you're going to have to
explicitly reference all those things worse because just adding those references
isn't so bad that's just like we used to do it with
the old project file format. Worse there's currently
some issues I can go out and show you this GitHub issue
where right now you have to explicitly link those files the automatic link
generation isn't working. So in order for some
XAML built processes, you actually need to add
a link element to your pages so that they show up in
the right relative path from your project files
point of view. So you might say, "I'm going to reference../old
projects/mainwindow.XAML." But you also need to add a link
element to that saying that we'd link it to just main Window.XAML so that it doesn't see
that directory traversal because that will mess up
some of the WPF built stops. So again, that's totally
solvable not blocking at all. It just means if we
go this route there's a few extra steps we have to take. So eventually, a year from now
six months from now whatever, we're just going to replace the old project file
with a new one that multitarget everything will be great. In the interim, we have to decide
between these other two options both of which have a couple of rough edges but should
it be blocking? So just a heads up about that. So for this demo let's go ahead
and create a new project. We're going to actually use in
the same directory approach, because I know they've
had some other videos recently where people put
them in separate directories, and so just for variety's sake. Let's see what this path looks
like and we'll go from there. So, here we are at visual studios. So we need to create a
new .NET Core project. This could just be right-click
"Add New Project.'' But again, I want to put in
the same folder and I don't want like the program.Cs it gets
auto-generated all that stuff. So I'm actually just going to
create the Csproj directly and rather than created by hand
which would be one option, I'm going to go to
a temporary directory. I'm going to say .NET
new WPF this way, I get the template and I don't have to remember what the SDK is called
and stuff like that. Because you can see
here's the contents of this auto-generated Csproj file. It's got that nice
Windows desktop SDK, it's got .NET Core App 3.0 is the target framework
WIN XL put uses WPF true. This is what we want our project
file to look like initially. So I'm going to become into VS code, add a file and call it BeanTraderClient.Core.Csproject
because I can't call it Bean Trader Client.Csproject
that would conflict. I'm just going to paste
this in. There we go. We've got a CS platform now for
a simple app maybe a Hello, World or something a little bit
more complicated we would be done. Because if I come over
into Visual Studio and I add that project to my solution, what you're going to see, let's see come in here and add it. You'll see that we
already have all of the C-Sharp sources referenced
by Rezek files are here, we've got our XAML in the views
folder because a lot of that stuff automatically included
by the new project file format. It automatically looks
for those and include them in the right way in the project. In a more complex project like this one though we're
going to want to go through and actually look at the old Csproj to see if there's
anything else we have to change, because there will be some things that will be
a little bit different here that we want to make sure are done right
in the new project file. So what I'm going to do is I'm
going to come over here, VS code, I'm going to open both the
old and the new project file, I'm going to put up side-by-side. Let's go ahead and look
through the old project file to see which parts of it we need to bring over
into the new project file. So let's go through and see
which of these properties and items we need in our new Csproj. So we've got these imports. We don't need this because
we referenced an SDK and an SDK attribute on the project will imply that we're
importing prompts, the targets that are necessary
to build this type of a project. So that takes care of
the standard imports. So we skip over that. We do probably want
root namespace and assembly name, because if we add new files we
want them to use that namespace by default and we want the app to build be called BeanTraderClients it looks exactly like the old one. Not BeanTraderClient Core despite
the different Csproj name. We don't need to auto-generate
binding redirects because .NET Core doesn't
use binding redirects. I don't use these constants in my project but this reminds me that I do probably
want to add a constant. Because I find it's useful to define a constant indicating that
we're building for .NET Core, so that if we have
sources shared between the .NET Framework and
.NET Core projects, in some cases we may need
to do one thing for .NET Core one thing for .NET
Framework if they're small API differences or something. So having this constant
available makes it easy to use a pound if to do that. >> There's Application
icon, we need that, so we'll bring our app icon over. Here's the things
that we're building. Most of these we only need, like the application definition
including app.xaml, that's done by default, all C# files in or under this directory or
compiled by default, so we skip those, XAML pages are included by default, so we skip those. Here's a resource. Embedded
resources are included, but resources are not. So we need an item group, where we can add this resource. We also have to add content. Well I mean some things will be
added by default as content, but this one is interesting
because it's a XAML file, but I'm adding it as content. The reason I'm doing this is
because I'm using my apps theming, to change like the color scheme or the accent of my app dynamically, and customers could choose
which theme they want. But I'm actually loading
some of the themes from disk so that we can have a style defined in XAML on disk and
then at run time we load that dynamically and
we use it as a theme. So that means that this XAML
file does not need to be a page, but it should be content. So I'm actually going to copy it as content because actually if I come
over and look at Visual Studio, you will see that XAML page is right now included as a page because that's
what happens by default. So instead, we're going
to add it as content, and I'm actually going
to remove it as a page. I'm not only gotten to
remove default accent.xaml, I'm going to remove, I mean use a globing patterns like you can with
the new project files, I'm going to remove that
file in any folder, not just the resources
themes defaults, so that way after we copy it to our output directory or to
an intermediate output directory, we won't start building it as
a XAML page from there either. These ones are fine. Assembly info.cs is
an interesting one, it's included automatically,
so that's good, but if we look at what
assembly info.cs is, it has a bunch of
assembly attributes, and this was part of
our new project template when we created the WPF app
originally on.Net framework. It has these attributes
for things like assembly, title, copyright, culture and so on. These assembly level attributes
are auto-generated in.Net Core based on properties
in your project files. So we actually can run
into an issue here because these attributes will conflict with
the auto-generated ones. Now, if this was a new project, it wouldn't be a problem because we wouldn't have assembly info.cs, we would just set properties in our Csproj to define those values, but in this case, it
does become a problem. So I'm going to add
a property called generate assembly info and I will change that to false instead
of its default true. What that means is that
we're not going to auto-generate those
assembly level attributes, and therefore we'll just
use this one instead. Other things that
might be interesting, the AppConfig is
included automatically, embedded resources are
included automatically. Resources are not like I said, if we come over here
and we can take a look at the images and these images
can build type none, so they're in the none group. In fact, I want them to be resources so that they're
embedded in my assembly, so I need to add more resources here. But again, we could use
that globing patterns. So instead of adding
each image individually, I could add all PNG files
anywhere under the directory, the CS projects, or if I
want I could say just under resources or resource images,
you can scope it. But I'm going to include
all the PNG files as resources. Finally, remember I said
in the last video that by switching our NUGET dependencies
to use package reference syntax, instead of using
a packages that config file that would make
things easier later. Well, here's what it makes it easier, it means that I can copy and just paste that item group
and this is going to work in this Csproj, the same as it would've
worked previously. So we don't need to
make any changes here, we just pick up our do
our NUGET references, we plop them down here
and we are good to go. So at this point, the Csproj file
is essentially done, the only thing that's left
to be done is to update the NuGet packages to
the right versions or to change them if we
need to change them. So I can come out to my command
prompt here and to a .NET restore, and I've specified which
projects since I have to Csproj files here now and I can do this for
Visual Studio as well. There's a lot of stuff that
can be done either from the Visual Studio IDE or
from the command prompt, both are great, it's
just a matter of preference. So I'll bounce back and
forth so you can see both ways for a lot of
the operations that I'm doing. In this case, I did a .NET restore, I get some warnings that there are packages that are targeting .NET framework that I'm using
instead of .NET Core. Remember, we talked about this
previously that's expected. This is the point where I'm going
to need to update some of those. There's different ways
we can update them. One way is to do a .NET add, and then I specify the
package that I'm adding to. I then say package, and I need to give
the name of the package, so I'll say MahApps.Metro, and this will update
the My Apps reference, actually it will updated to
the latest stable version. In our case we don't
want stable version, we want the one we found out on
NuGet that's supported .NET Core, so I will search for MahApps.Metro, it's this Tuo alpha that we want. So I'll actually copy that version
so I get the version right, we will add that. So by doing a .NET AD package, I'm now updating the package
version that we're depending on to a more recent one
which supports .NET Core. If we come over to Visual Studio, we can do the same thing from here. We go to Manage NuGet packages, see the ones that are installed. So we can take things like
the Microsoft Azure common, I know the 2.0 version did not
support .NET standard or .NET Core, but the latest stable one
does support that. So we're going to update, say "Yes", let that update. Then you can see here some
of the other warnings, there's Microsoft Identity Model, Clients Active Directory did not
target .NET frame or .NET Core, but the newer version does, so I update that one to 451, instead of version 229. Then the Neato async is another one, that has a pre-release
that supports .NET Core. So I'm going to update
from 401 to 500 pre05. That should be all of my packages
that need to be updated. Again, in the first step, we went through and understood
what our packages were, so that we knew if we were
going to have any issue. So we know now what needs updated and we can do
those updates pretty quickly. Let's do one more .NET restore.
I have to save changes. When I make changes with
the new get package manager here, it doesn't actually save the changes until I hit that "Save" button. So now I'll do a .NET restore. I still have one warning. There's this Microsoft XAML
behaviors WPF, so what is that? I'm not depending on that directly, it must be a transitive
dependency that comes in, thanks to the closure
of dependencies of things that I depend
on at a top level. An easy way to find out how
things are being pulled in, is to go look in your object folder, there should be a file
called Project.assets.JSON. This is produced when
you do a .NET restore, and it shows everything
that's pulled in from NUGET. The're libraries, there's the files, which packages they're part of, why those packages are referenced. So I can search for
that Microsoft XAML behaviors, I see it is included because it's a dependency of
the controls EX package. Controls EX in turn is included because it's
a dependency of MahApps.Metro. Okay. So this is interesting. My project is depending on a NuGet package that
targets .NET framework instead of .NET Core. You remember I said that's
not really what you want, it's okay, but it's not best. In this case, there's
not a lot I could do about it because it's
not a direct dependency. If I really wanted to use
a different version of this Microsoft XAML
behaviors WPF library, I could add a reference to
a particular version of the NuGet package as a top-level dependency and then
everything would use that. But in this case, I don't think
there even is a newer version, that's the latest version, so there's nothing to switch to. But because it's not
a top-level dependency, it's more likely to be okay. MahApps.Metro with their version 2.0 has said this targets .NET Core. So they have a dependency which
depends on .NET framework, but presumably when they
targeted .NET Core, they test it on that platform,
and they said, "Yeah. This works on .NET Core,
it's safe to use." So I'm going to trust the owners
of the packages I'm depending on directly and say if they're
pulling at something transitively that targets .NET
framework, it's probably fine. Because again, many .NET
framework packages can be used, and this one probably is safe, at least in the ways that MahApps.Metro it
controls EX or using it, otherwise they would have
trouble targeting .NET Core. So this particular
warning is probably safe and we can consider our project
file done at this point, it looks a lot like our old
project file, but way smaller. We automatically include
a bunch of stuff, we make a few modifications with
globing patterns and we've got our references to
other packages or projects. That's really all you need to do, and now we have two project files, one that targets .NET framework, one that targets .NET Core. Next stop, we're going
to do a .NET build, and we're going to just start
going through build errors. That's the majority of the work
in a migration efforts, so that's probably can be two videos. So I think coming up in
videos three and four, we're going to be doing the code level changes that we need to
make this app work on .NET Core.