Video: Migrating a WPF App to .NET Core 3 (5/5)
>> 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 csproj , 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 that packages so that your CS Proj has all the right content in it. You also at this point, can update your NuGet packages to make sure that you are 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 issue. 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 updated version of Service Util, which will support.NET Standard and.NET Core. So at this point, we could build the project. A lot of people might think we're done now since the compile is clean. But in fact, there's a fourth step which is important and that's running and testing the app because there are a lot of differences, but 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. We actually have some analyzers to help with this. If you can look at github.com/. net/platformcompat. These are Roslyn 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 that's not something you would need for WPF app, but in other scenarios, it may be useful to know how Core 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 is our nice clean build we had last time. So now, we would actually run it. So I'll hop over here, and I've made sure that my.NET Core project is the startup project. I will hit ''F5'' to run. Let's see what happens. We hit an exception. It says ''Configuration system failed to initialize.'' There's an inner exception here unrecognized configuration section system.servicemodel. Okay. So something is wrong with our app config. 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 the connected services or.NET Service Util, this will actually be done for us in the client based on the endpoint we select. See here, we're setting up our endpoint address, here, we're setting up the binding. So all the stuff that was being done through configuration is now done through code in our WCF client. So we actually can remove this. Now, 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 a Configuration Manager, this works on.NET Core, and we can use it in our app, 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 at.NET Standard. They are available.NET Core not on.NET Standard. So that means things like 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 if you want to configure using configuration file, you still can have an 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 S.Net Core style configuration using Microsoft extensions configuration because that doesn't 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 appconfig, we just can't use the WCF specific parts of that. Okay. So let's go ahead and hit ''F5'' again and 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 the.NET Framework, one was still using the old client where we were reading configuration from app config, we would end up having to 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. Another exception, operation is not supported on this platform. So we can take a look at the stack trace here. Function of begin invoke. Well, it turns out that delegate.
begininvoke 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, 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 invoked and end invoke since it's not possible to support them on.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 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, the point is we can't use begin invoke. But like I said, there are better ways to do this now. First option, if you're calling an API that has an async alternative, just await an async call. In fact, in this case, 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, what was it we were calling? Get current trader info, tells us that's a slow, but get current trader info async, and we can say current trader info. This right here is the correct way to do it because in general, async methods are going to be superior to the call and 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, this case, I'll pretend like I don't, because at a lot of times you won't, and I want to show what that looks like. You can use Task.Run as an alternative. Task.Run is able to run some action or some funk on a different thread for you in the same way the begin invoke would, but it does it without using that remoting infrastructure under the covers. So it continues to work on.NET 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 the same delegate, this UserInfo 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.Run, and this now is going to be equivalent to call UserInfo retriever.
begininvoke. 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 our task is this, or we'll even call it results so that we get the same naming as before, then I can do await result. Another option is we can say Task.Run.continuewith, and continue with will provide a continuation task for after 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 going to 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. This should be equivalent because now we're running the invoke method asynchronously with Task.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 in this ''continue with'' expression. At this point, it's complaining that task scheduler which I will add.
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 a comment it out, not allowed. I want to get rid of that. So let's delete it and ''F5.'' 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 has logged me. We're 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. Again, I feel like I want to get even more green beans. I can cancel trades. Let's see if we can sign out. We can 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.NET 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.NET shared Microsoft.NET Core app 3.0. So this is in fact, running on.NET Core not.NET Framework. I know that this was a long series of videos, but really, if I wasn't talking through it this would have been an hour. Again, it's a simple app, but 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.NET Core. I 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 API Port analyzer, and the Compat analyzers, but I hopefully it gives you an overview of what the process looks like for moving from.
NET Framework to.NET Core..