Breathing new life into a “legacy” ASP.NET website is a way you can help slowly transition the site towards ASP.NET Core. One way you might accomplish that is by referencing .NET Standard libraries. Everything seems fine and dandy until you attempt a publish operation. Generally you might not notice these until running msbuild from command-line–such as in a CI environment, batch deploy script, or otherwise. Let’s talk about one major msbuild error you’ll encounter while publishing your ASP.NET MVC application that references a .NET standard library. We’ll also talk about some other errors that might arise in the process.
Code for today’s post can be located on my GitHub. I have the failing project as
master and the fixes applied in the
Setting the stage
On a couple occasions (Using WCF with .NET Core and Throttling requests in .NET Core web applications) I’ve mentioned recently undergoing the upgrade of an ASP.NET Core 2.0 site to 2.2. That upgrade had a bunch of speed-bumps. One such speed-bump I hadn’t mentioned was that my deployment scripts started having errors. See below for an example of the error:
C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\Microsoft.NET.Build.Extensions \Microsoft.NET.Build.Extensions.NETFramework.targets(67,5): error NETSDK1050: The version of Microsoft.NET.Sdk used by this project is insufficient to support references to libraries targeting .NET Standard 1.5 or higher. Please install version 2.0 or higher of the .NET Core SDK. [D:\dev\TestApplication\TestApplication.csproj]
(ImplicitlyExpandNETStandardFacades target) -> C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\Microsoft.NET.Build.Extensions \Microsoft.NET.Build.Extensions.NETFramework.targets(67,5): error NETSDK1050: The version of Microsoft.NET.Sdk used by this project is insufficient to support references to libraries targeting .NET Standard 1.5 or higher. Please install version 2.0 or higher of the .NET Core SDK. [D:\dev\TestApplication\TestApplication.csproj]
Reproducing this is pretty easy. Create a brand new ASP.NET MVC application and target anything
4.7 or lower. For my example I’m using 4.7. Next create a
netstandard2.0 libray and reference it to your MVC app. Lastly, try to publish it. Boom. Fail.
Luckily it is also not all that difficult to fix if you have control over your target .NET framework. If not… uh… sorry.
Upgrade your target framework
It is literally as easy as opening the properties for your ASP.NET MVC web application, going to the Application tab, and then switching the framework to
4.7.2. Sure, partial support for
netstandard2.0 was added for
4.6.1 so you’d think this would work. It doesn’t. Microsoft suggests you upgrade all the way to 4.7.2 (see graphic below). See also Rick Strahl’s post about it.
Here’s the thing though… while technically that is “enough” to get it working, you probably don’t actually want to stop there. Inevitably you will have assembly version mismatches and other issues arise. Standard libraries also use the PackageReference style instead of package.config which is another potential source of consternation for you. Let’s talk about a couple more changes you can make.
Switching from packages.config to PackageReference
One of the best things (IMHO) you can do to save some trouble down the road is switch any of your non-core/non-standard libraries to PackageReference. Doing so in Visual Studio is easy for most project types. Unfortunately ASP.NET MVC applications (at time of writing) is not supported.
You can do it manually or you can cheat. I choose to cheat. But Jon, how can I cheat, you ask? Simple. Download the NuGet PackageReference Upgrade extension by CloudNimble. Instead of clicking the “Migrate packages.config to PackageReference…” in the context-menu, you’ll use the “Upgrade to PackageReferences” menu option.
I imagine there are use-cases where it doesn’t work. I haven’t hit one yet.
Automatic Assembly Binding Redirects
One thing that you’ll have (hopefully) noticed when you upgraded your target framework is there is a checkbox for
Auto-generate binding redirects. To be perfectly honest I can’t recall when that even showed up as an option in the IDE. When you switch to
4.7.2 it automatically checks it so I presume that is the default option. That being said, I sometimes have a hard time trusting things. Maybe it’s because I’ve been burned before?
Here is a small change you can add to your csproj file.
<PropertyGroup> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <RestoreProjectStyle>PackageReference</RestoreProjectStyle> </PropertyGroup>
All this is going to do is have a hard reference to ensure the project is PackageReference and flat out states that you expect it to auto generate your binding redirects.
Well great, but now it won’t run
I ran into a fun issue where attempting to run the site after all this showed an error about System.Object being referenced in another library that wasn’t reference in my project. Error was something scary like:
Error CS0012 The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=18.104.22.168, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. Luckily this issue report talks about what I hit.
Let me spare you the details. Just go into the compilation section your web.config for the ASP.NET MVC application, add an assemblies tag if it isn’t there, and make sure it references
<add assembly="netstandard, Version=22.214.171.124, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />.
Binding redirect leftovers
Ok so I just told you that you should auto-generate some binding redirects, didn’t I? I ran into a seemingly edge-case scenario where everything worked peachy in development. However, when I published and loaded the site in a test environment it failed. Turns out that since this was a pretty old application that had been upgraded over and over again, I still had a ton of manual redirects in my web.config. You can take the scorched earth approach and remove them all (which I don’t recommend), or you can remove them one-by-one as you hit them in your testing environment. Eeek!
The main one I personally ran into was
System.Net.Http. At some point I (or a package upgrade) added the following. You just need to remove it.
<dependentAssembly> <assemblyIdentity name="System.Net.Http" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"/> <bindingRedirect oldVersion="0.0.0.0-126.96.36.199" newVersion="188.8.131.52"/> </dependentAssembly>
Sample build script
This is a really simplified version of a batch file build script. If you check my source you’ll see this resides as
Publish.bat inside a deploy folder under the main source tree.
@echo off SETLOCAL if not defined DevEnvDir ( call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\Tools\vsdevcmd.bat" ) if not defined DevEnvDir ( call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\vsdevcmd.bat" ) msbuild ../MvcWithNetStandard.sln /p:DeployOnBuild=true /p:PublishProfile=PublishRelease /p:VisualStudioVersion=15.0 /p:Configuration=Release /m ECHO Ready for Deploy pause ENDLOCAL
Extending “legacy” applications with netstandard is a way to help walk your project towards a future migration but it can come with it’s own set of problems. Today we discussed one way to quickly fix the issue and another way to help prevent future errors.
Code for this blog post can be located on my GitHub.