Thursday, 24 January 2019

15 Lessons Learned while Converting from ASP.NET to .NET Core

At Stackify we have been doing a lot of work with .NET Core over the last few months. We ported StackifyLib and our logging appenders over. We have also made sure that our app performance tools, Prefix and Retrace, can be used to profile .NET Core based apps. In fact, we’ve even converted the entire codebase of Prefix to core so it can run on a Mac! Below are some of the things we have learned to help convert ASP.NET to .NET Core.

Best Practices on Migrating ASP.NET to ASP.NET Core

1. Using xproj & csproj files together

There doesn’t seem to be any way for these two project types to reference each other. You move everything to xproj, but then you can no longer use MSBuild. If you are like us, that means your current setup with your build server won’t work. It is possible to use xproj and csproj files both at the same time which is ultimately what we ended up doing for our Windows targeted builds of Prefix. Check out our other blog post on this topic.

2. Building for deployment

If you are planning to build an app that targets non-Windows, you have to build it on the target platform. In other words, you can’t build your app on Windows and then deploy it to a Mac. You can do that with a netstandard library, but not a netcoreapp. They are hoping to remove this limitation in the future.

3. NetStandard vs NetCoreApp1.0

What is the difference? NetStandard is designed as a common standard so that .NET 4.5, Core, UWP, Xamarin and everything else has a standard to target. So, if you are making a shared library that will be a nuget package, it should be based on NetStandard. Learn more about NetStandard here:
If you are making an actual application, you are supposed to target NetCoreApp1.0 as the framework IF you plan on deploying it to Macs or Linux. If you are targeting Windows, you can also just target .NET 4.5.1 or later.

4. IIS is dead, well sort of

As part of .NET Core, Microsoft (and the community) has created a whole new web server called Kestrel. The goal behind it has been to make it as lean, mean, and fast as possible. IIS is awesome but comes with a very dated pipeline model and carries a lot of bloat and weight with it. In some benchmarks, I have seen Kestrel handle up to 20x more requests per second. Yowzers!
Kestrel is essentially part of .NET Core which makes deploying your web app as easy as deploying any console app. As matter of fact, every app in .NET Core is essentially a console app. When your ASP.NET Core app starts up, it activates the Kestrel web server, sets up the HTTP bindings, and handles everything. This is similar to how self hosted Web Api projects worked with Owin.
IIS isn’t actually dead. You can use IIS as a reverse proxy sitting in front of Kestrel to take advantage of some of it’s features that Kestrel does not have. Things like virtual hosts, logging, security, etc. Microsoft still recommends using IIS to sit in front of your ASP.NET Core apps.
Check out this blog post about deploying to IIS: Publishing and Running ASP.NET Core Applications with IIS
If you have ever made a self hosted web app in a Windows service or console app, it all works much differently now. You simply use Kestrel. All the self hosted packages for WebApi, SignalR and others are no longer needed. Every web app is basically self hosted now.

5. HttpModules and HttpHandlers are replaced by new “middleware”

Middleware has been designed to replace modules and handlers. It is similar to how Owin and other languages handle this sort of functionality. They are very easy to work with. Check out the ASP.NET docs to learn more. The good (and bad) news is you can’t configure them in a config file either. They are all set in code.

6. FileStream moved to System.IO.FileSystem ???

Some basic classes that everyone uses on a daily basis have been moved around to different packages. Something as common as FileStream is no longer in the System.IO assembly reference/package. You now have to add the package System.IO.FileSystem. This is confusing because we are using class namespaces that don’t directly match the packages.
This website is very valuable for figuring out where some classes or methods have been moved around to: http://packagesearch.azurewebsites.net/

7. StreamReader constructor no longer works with a file path

Some simple uses of standard libraries have changed. A good example is the StreamReader which was often used by passing in a file path to the constructor. Now you have to pass in a stream. This will cause small refactorings to use a FileStream in addition to the StreamReader everywhere.
Another good example of this is around reflection. GetType() now returns a more simplified object for performance reasons and you must do a GetTypeInfo() to get the full details. Luckily that is backwards compatible to .NET 4.5

8. Platform specific code… like Microsoft specific RSA

.NET Core is designed to run on Windows, Macs and Linux. But some of your code could potentially compile on Windows but then fail at runtime when you run it on a Mac or Linux. A good example of this is RSACryptoServiceProvider which appears to be useable. At runtime on a Mac you will get a “platform not supported” type exception. Evidently this RSA provider API is Windows specific. Instead you have to use RSA.Create() which is a more generic implementation and has slightly different methods. Both are in System.Security.Cryptography. Confusing huh? The old “If it builds, ship it!” mentality totally falls apart on this one!

9. Newtonsoft changed to default to camel case on field names 🙁

This has to be one of the biggest headaches of the conversion. Newtonsoft now defaults to camelCase. This will cause all sorts of REST APIs to break if you were using PascalCase. We ended up using the JsonProperty attribute on some things to force their casing how we needed them. This one is a big land mine, so watch out for it. #TeamPascalCase

10. Log4net doesn’t (didn’t) work and neither do countless other dependencies unless you target .NET 4.5!

Log4net is a pretty fundamental library used by countless developers. It has not been ported to core, yet. NLog and Serilog work and you will have to switch logging providers. Before converting anything to core, you need to review all of your referenced dll dependencies to ensure they will work with core. But as long as you are targeting Windows, you can target .NET 4.5.1 or newer and use all your current dependencies! If you have to go cross platform… watch out for dependency problems.
Be sure to check out our entire article about ASP.NET Core Logging.
Update: log4net has been updated to work with .NET Core

11. System.Drawing doesn’t exist

Need to resize images? Not with the .NET framework currently.
There are some community projects that you can use. Check out Hanselman’s blog post: Server-side Image and Graphics Processing with .NET Core and ASP.NET 5

12. DataSet and DataTable doesn’t exist

People still use these? Actually, some do. We have used DataTables for sending a table of data to a SQL stored procedure as an input parameter. Works like a charm.

13. Visual Studio Tooling

We have seen a lot of weirdness with intelligence and Visual Studio in general. Sometimes it highlights code like it is wrong, but it compiles just fine. Being able to switch between your framework targets is awesome for testing your code against each. Although we just removed net451 as a target framework from my project, but Visual Studio still thinks we are targeting it…. There are still a few bugs to be worked out.

14. HttpWebRequest weird changes

In .NET 4.5 there are some properties you have to set on the HttpWebRequest object and you can’t just set them in the headers. Well, in core they decided to reverse course and you have to use the header collection. This requires some hackery and compiler directives… Otherwise you get errors like this from your .NET 4.5 code: The ‘User-Agent’ header must be modified using the appropriate property or method. We need some extension methods for core to make it backwards compatible.

15. Creating a Windows Service in .NET Core

Windows Services can be easily created with Visual Studio 2017 as long as your code targets the full .NET Framework. You will need move your .NET Core code to a class library that targets NetStandard that can then be shared via other .NET Core apps and your Windows Service.

16. Web API is Gone/Part of MVC Now

With .NET Core Microsoft and the community decided to merge Web API and MVC together. They have always been very similar to work with and either could be used for API type applications. So in a lot of ways, merging them made sense. Check out our detailed article on this subject about Bye Bye ASP.NET Core Web API and our list of top ASP.NET core features.

BONUS – Database access

Low level access via SqlConnection and SqlCommand works the same. My favorite two ORMs, Dapper and Entity Framework, both work with dotnet core. Entity Framework Core has quite a few differences. Learn more here: https://docs.efproject.net/en/latest/efcore-vs-ef6/features.html

No comments:

Post a Comment