 Okay, we're in the home stretch now. In the last video, we got our sample application building for .NET Core. So that means that we're ready to go on to the last step of the migration process to get our bean trader sample running completely on .NET Core 3. As you remember, I described the migration process as having four steps. First, you prepare for migration by understanding your dependencies, both framework dependencies with the portability analyzer, and third party dependencies by just reviewing them. And you get your NuGet package references into the new package reference format, which is used for .NET Core projects. In the second step, you create that new CS Proj. You decide whether it's going to live next to your existing one or in a different folder. You add all of the resources, the custom build steps, the NuGet packages, so that your CS Proj has all the right content in it. And you also, at this point, can update your NuGet packages to make sure that you're using a version that supports .NET Core or .NET Standard. In the third step, which is what we did in the previous two videos, we fix build time issues. So we make sure that our project can actually compile successfully against .NET Core. We go in, we fix up small API differences between .NET Framework and .NET Core in the framework itself, as well as maybe in the NuGet packages we're depending on. So we make sure that we can build both the new and the old projects. Also in this step, you may remember we regenerated our WCF client to be generated by an updated version of Service Util, which will support .NET Standard and .NET Core. And so at this point, we can build the project. A lot of people might think we're done now since it compiles clean. But in fact, there's a fourth step, which is important. That's running and testing the app. Because there are a lot of differences, maybe not a lot, there are some differences between .NET Framework and .NET Core that only manifest at runtime. You may get not supported exceptions or something like that. And we actually have some analyzers to help with this. If you come look at github.com slash .NET slash platform, compact, these are Roslin analyzers that will identify APIs and code patterns in your project that might build successfully, but will cause issues at runtime on .NET Core. It also identifies APIs that are supported by .NET Core on some platforms, but not other platforms. So, you know, that's not something you would need for a WPF app. But in other scenarios, it may be useful to know how cross-platform your solution will be. So those analyzers can help. Let's go ahead and do this with our bean trader sample. Okay, so here, here's our nice clean build we had last time. So now we would actually run it. So I'll hop over here and let's, okay, I've made sure that my .NET Core project is a startup project. I will hit F5 to run. Let's see what happens. And we hit an exception. It says configuration system failed to initialize. There's an inner exception here, unrecognized configuration section, system.service model. Okay, so something's wrong with our app config. And this makes sense if we come look here. You remember in the last video, I was talking about one of the differences in using WCF on .NET framework versus on .NET Core. Is that in .NET Core, the WCF client is not configured using app configuration. None of this is respected. You have to do it programmatically instead. But if we look at our client that we generated, the new WCF client coming out of, you know, the connected services or .NET service util. This will actually be done for us in the client based on the end point we select. So here we're setting up our endpoint address. Here we're setting up the binding. So all of the stuff that was being done through configuration is now done through code in our WCF client. So we actually can remove this. Now, you know, the difference between doing something by configuration doing any code is something you may see somewhat regularly with .NET Core. Because this configuration system where we're using the app.config and we access the values with the configuration manager, this works on .NET Core and we can use it in our app. And in fact, we are using it to read these app settings. But libraries that want to run on all .NET standard platforms can't depend on this because the same configuration APIs aren't all available on .NET standard. They're available on .NET Core, not on .NET standard. So that means things like, you know, system service model, WCF. If WCF wants to work across all .NET standard platforms, this can't be the way that configuration is done. So, you know, if you want to configure using configuration file, you still can have app settings and you can programmatically set up your WCF client using those settings. But in general, you're going to see less of an emphasis on app.config just because it doesn't exist on .NET standard. Instead, in .NET standard, you might use the new espionet core style configuration using Microsoft Extensions configuration because that does work on .NET standard. In this case, though, we're only running on .NET core, so it's safe for us to use configuration manager and app config. We just can't use the WCF specific parts of that. Okay. So let's go ahead and hit F5 again. See if we get a little farther this time now that we have removed that config section. By the way, that's another reason that it's good that we have our .NET framework and .NET core versions of the app both using the new WCF client because if only the .NET core version was using it, the .NET framework one was still using the old client where we were reading configuration from app config. We'd end up having two app config files, one for .NET framework with WCF stuff and one for .NET core without it. Anyhow, we have hit F5 and our app has launched. This is good. Let's go ahead and log in. Ah, another exception. Operation is not supported on this platform. So we can take a look at the stack trace here. Function begin invoke. What turns out that delegate.begin invoke and end invoke are not supported on .NET core. There's a good discussion about why this is out on GitHub if you want to go read up on it. Got lots of details. It's in the .NET core effects issue number 5940. Basically what it comes down to is these APIs have a dependency on remoting infrastructure which just doesn't exist on .NET core. So you can't use them. But there are a lot of more modern alternatives that are better anyhow. So it shouldn't be too big a deal to get away from begin invoke and end invoke since it's not possible to support the .NET core. Now, one interesting thing about these is that they don't turn up in the API port report. So you might not realize that you had this dependency even though because the portability analyzer didn't call it out. The reason for that is because these are defined on the delegate types and many times delegates are defined in user code and they will automatically have a begin invoke and end invoke method that was generated by the compiler and those still won't work even though they're in user code but because they're in user code API port doesn't see them. So anyhow, point is we can't use begin invoke. But like I was saying there are better ways to do this now. First option, if you're calling an API that has an async alternative just do it just await an async call. In fact, in this case, I'm, you know, this is a little bit contrived. I'm using begin invoke to call a delegate on a method that's actually an async method which is not something you're likely to see in the real world because of course the trivial fix for this is to await trading service dot what was it we were calling get current trader info my intelligence is slow but get current trader info async like this you know and we can say current trader equal. This right here is the correct way to do it because in general async methods are going to be superior to calling begin invoke. In the worst case they both just go run it on a different thread and a lot of cases if there is an async alternative this is going to actually be non-blocking which begin invoke typically isn't. So if you have an async alternative call the async alternative. Now in this case I'll pretend like I don't because a lot of times you won't and I want to sort of show what that looks like. You can use task dot run as an alternative task dot run is able to run some action or some funk on you know on a different thread for you in the same way that begin invoke would but it does it without using that remoting infrastructure under the covers so it continues to work on dotnet core. So all you need to do is you can replace your begin invoke with a call to invoke because that should work so I can take this same delegate this this user info retriever funk and I can still use it but I don't want to call begin invoke instead I just call invoke in a task dot run and this now is going to be equivalent to calling user info retriever dot begin invoke. Now in this case we take the result and we do something with it so there's different ways I can either await that here I can say you know var task is this or you know we'll even call it result so that we get the same naming as before and I can await result. Another option is we can say task dot run dot continue with and continue with will provide a continuation task for after this one this initial one completes which is very similar to what's being done here where we're invoking this delegate and then after it completes we run some code so we can actually do almost exactly the same thing here we're gonna say continue with get our result and I can just copy the exact same code we had before but drop in result instead of end invoke and this should be equivalent because now we're running the invoke method asynchronously with task dot run and when it finishes then we run our follow-up here instead of calling end invoke I just get the result as a parameter you know in this continue with expression so at this point okay it's complaining that I need a task scheduler which I will add and okay this should be equivalent code that doesn't use the begin invoke and end invoke APIs which aren't supported on core so let's go ahead and f5 again see what happens actually I should probably just delete this code we don't need to comment it out it's not allowed you know what I want to get rid of that so let's delete it and f5 okay it's building it's launching it's doing stuff okay here we are our app has started up I'll log in as Mike it is logged me in we are communicating with our WCF backend we're displaying data I can accept trades here I'll accept this trade from Daniel okay I now have five more green beans and 20 less blue beans I can make my own trades maybe I feel like I want to get even more green beans do this I can I can cancel trades let's see if we can sign out we sign out sign in again accept a trade sign out sign in at this point I think we can say that our app is working correctly and the most important part is it's working correctly on dotnet core if I come over here to the debugger and I look at the modules you can see that these modules are all loading from dotnet shared Microsoft net core app 3.0 so this is in fact running on dotnet core not dotnet framework and you know I know that this was a longish series of videos but really I mean if I wasn't talking through it this would have been an hour and this again it's a simple app but you know hour hour and a half of coding we've taken a WPF app that uses WCF client functionality it uses Castle Windsor my apps Metro and so on and we have ported it to work exactly the same on dotnet core hope this was a useful overview just to review here's the steps we went through I'll be sure in the comments below to link useful docs as far as the migration process a blog where we talk about all of this some of the tools like the API port analyzer and the compact analyzers but hopefully it gives you an overview of what the process looks like for moving from dotnet framework to dotnet core