12

Adding EF Core to a NET Standard project introduces transitive dependency versions incompatible with NuGet packages in other projects

I have a solution with multiple .NET Standard 2.0 projects.

One Project A uses the Google.Protobuf (3.11.2) NuGet package, that depends on

System.Memory (4.5.3)
    System.Buffers (4.4.0)
    System.Numerics.Vectors (4.4.0)
    System.Runtime.CompilerServices.Unsafe (4.5.2)

A few other projects also depend on System.Memory and all use the same dependency versions.

Another Project B uses Microsoft.EntityFrameworkCore (3.1.0) NuGet package that depends on

System.Memory (4.5.3)
    System.Buffers (4.5.0)
    System.Numerics.Vectors (4.5.0)
    System.Runtime.CompilerServices.Unsafe (4.7.0)

Even though the System.Memory version is (4.5.3) in both cases, it depends on System.Buffers, System.Numerics.Vectors and System.Runtime.CompilerServices.Unsafe and their versions differ.

When I run the application that uses these projects (a Microsoft Prism WPF .NET Framework 4.8 app that uses Unity IoC) UnityContainer throws the following exception:

System.IO.FileLoadException: 'Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference.

After searching for a solution I added this to my NuGet.Config:

  <config>
    <add key="DependencyVersion" value="Highest" />
  </config>

In both, %appdata%\Nuget and in the root folder of the .sln file.

I also deleted the %userprofile%\.nuget\packages folder.

Then I removed the NuGet packages from the projects and added them back again, but their dependecies come with the same versions as before.

If I navigate to "Manage NuGet Packages for Solution..." in Visual Studio and choose "Consolidate" it just says "No packages found"

Jinjinov
  • 2,554
  • 4
  • 26
  • 45
  • Do you mean you have two SAME versions of System.Memory depending on different package versions?? – Pablo Recalde Jan 08 '20 at 11:00
  • @PabloRecalde Yes, that is exactly what I mean. But I didn't install `System.Memory` so I can't control its version - it was installed as a dependency of `Google.Protobuf` and of `Microsoft.EntityFrameworkCore` – Jinjinov Jan 08 '20 at 11:41
  • Seems like Microsoft messed up with versions ... It does not make any sense to me – Pablo Recalde Jan 08 '20 at 15:10
  • As a workarround you can try to overwrite the dll that does not match with others on the .nuget cache folder – Pablo Recalde Jan 08 '20 at 15:11
  • @PabloRecalde If I add the `Microsoft.EntityFrameworkCore` package to the project that uses `Google.Protobuf`, then the transitive dependencies of the `Google.Protobuf` change to be the same as with `Microsoft.EntityFrameworkCore` - my guess is that NuGet selects the lowest common version. Of course that is not a solution because I don't want the `Microsoft.EntityFrameworkCore` package in the project using `Google.Protobuf`. – Jinjinov Jan 08 '20 at 15:17
  • `.net standard` should not reference `.net core` packages. It shoould not have to either. Microsoft created `.net standard` as a bridge to be able to share logic between them. With `.net 5` https://devblogs.microsoft.com/dotnet/introducing-net-5/. If you want to read more about referencing i have provided an answer to this post with elaborate info about minimizing dependency errors -> https://stackoverflow.com/a/59445495/3902958. I am not sure i get 100% what you say, but hope this helps. – panoskarajohn Jan 12 '20 at 14:18
  • 1
    @panoskarajohn my .net standard projects do NOT reference .net core packages - `Microsoft.EntityFrameworkCore` is a .net standard library ;) you can easily reproduce my problem in 3 minutes, if you don't believe me :) – Jinjinov Jan 12 '20 at 14:36
  • First off, did only skim. Second, haven't tried to reproduce. Anyway, here goes. Can you maybe solve your problem with a binding redirect? I have one in one of my projects, albeit autogenerated: `` Binding redirects have also caused me trouble and simply deleting them has eliminated problems, too. – Heki Jan 13 '20 at 09:23
  • @Heki my solution has 54 projects that reference 43 NuGet packages - each of those packages has somewhere from 5 to 20 transitive dependencies... far too many to write binding redirects by hand (the senior developer and the project manager in my company don't approve this, because it makes the solution hard to maintain) can I get auto generated binding redirects? - there is another solution that can be written by hand: using a json file: https://github.com/NuGet/Home/issues/5887 – Jinjinov Jan 13 '20 at 10:07
  • @Jinjinov If redirects will solve the problem, then automatic generation looks like it will require a bit of work cause updating web.config is not standard. Take a look here https://stackoverflow.com/questions/48970018/why-doesnt-autogeneratebindingredirects-work-for-a-web-config-in-visual-studio – Heki Jan 13 '20 at 10:55
  • @Heki I have tried this solution, but it doesn't work - it appears that doesn't work for class libraries - here is an open issue: https://github.com/microsoft/msbuild/issues/1310 – Jinjinov Jan 13 '20 at 11:33
  • 1
    @Jinjinov Oh, that's a bummer. Last idea: https://blog.yaakov.online/automatically-generating-assembly-binding-redirects/ I hope that will get you going as it specifically mentions class libraries in the bottom. If not, sorry I couldn't help and I hope you reach a solution soon! :) – Heki Jan 13 '20 at 12:07

2 Answers2

14

I managed to reproduce the problem. I created two new .net standard 2.0 project class libraries.

On the first I added EF Core. On the second I added Google protobuf.

Both same versions as you mention.

For EF core I created a new class that just inherits from DbContext. For Protobuf I just created an empty class as I am not familiar on how to use it. I was still able to replicate the problem though.

Then I created a console app .net framework 4.7.2 referencing the above two projects.

I instantiated the two classes in the Console App and got error System.IO.FileLoadException: 'Could not load file or assembly...

How I resolved it

I went to all three projects and added this line to the csproj.

<RestoreProjectStyle>PackageReference</RestoreProjectStyle>

to the Property Group.

<PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>

After that I built and ran again and no error appeared.

Please let me know your results. Even if my solution does not work for you, I believe it is good practice to have it.

To quote Oren.
"Using .NET Standard requires you to use PackageReference to eliminate the pain of “lots of packages” as well as properly handle transitive dependencies. While you may be able to use .NET Standard without PackageReference, I wouldn’t recommend it."

Also Hanselman mentions: "The "full" Framework projects are using the older .csproj format and by default, they use package.config to manage dependencies. The newer projects can reference Packages as first-class references. So we need to tell ALL projects in this solution to manage and restore their packages as "PackageReferences.""

Here are my sources.

https://www.hanselman.com/blog/ReferencingNETStandardAssembliesFromBothNETCoreAndNETFramework.aspx

https://oren.codes/2017/04/23/using-xamarin-forms-with-net-standard-vs-2017-edition/

Updated according to Sommen's extra info from the github issues Kudos to Sommen for providing this extra info. Also Kudos to Immo Landwerth for providing this Info at GitHub. I will provide as is the Workarounds that already exist in the Github page just for complecity as advised by the OP jinjinov.

Taken from GitHub Issues

Workarounds

Regular .NET Framework projects

  1. Enable automatic binding redirects in the root .NET Framework application
  2. Make sure your root application project doesn't use packages.config but uses PackageReference for NuGet packages:
    • If you currently don't have packages.config, simply add <RestoreProjectStyle>PackageReference</RestoreProjectStyle>.
    • If you currently do have a packages.config, convert the contents to package references in the project file. The syntax is like this: <PackageReference Include="package-id" Version="package-version" />

ASP.NET web applications and web sites

  1. Web applications and web sites don't support automatic binding redirect generation. In order to resolve binding conflicts, you need to double click the warning in the error list and Visual Studio will add them to your web.config file
  2. In web application projects, you should enable PackageReference like mentioned above. In web sites, you cannot use PackageReference as there is no project file. In that case, you need to install all NuGet packages into your web site that any of the direct or indirect project references depend on.

Unit test projects

By default, binding redirects aren't added to class library projects. This is problematic for unit testing projects as they are essentially like apps. So in addition to what's outlined in automatic binding redirects you also need to specify GenerateBindingRedirectsOutputType:

<PropertyGroup>
    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
    <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>

There is also a discussion section that provides more information -> GitHub discussion

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
panoskarajohn
  • 1,846
  • 1
  • 21
  • 37
  • Thank you! Reading the link from sommmen https://github.com/dotnet/announcements/issues/31 I see that your solution works for projects that don't have any NuGet packages -- on GitHub issue see Workarounds, point 2. -- I assume that you didn't include any in your console app? If you update your answer to include all worarounds I will accept it, because you wrote it before sommmen did :) – Jinjinov Jan 14 '20 at 08:43
  • @Jinjinov thank you, I have updated the answer to provide the full info. If you feel there is more to it. Do not hesitate to edit and update. – panoskarajohn Jan 14 '20 at 09:25
4

Yeah, welcome to the struggle.

Like PanosKarajohn pointed out using packagereference instead of packages.config helps with this. Unfortunately, that is Vs2017 and up and for some of us that is not yet in sight.

The problem is actually explained pretty much here: https://github.com/dotnet/announcements/issues/31

You need to use binding redirects to redirect all the version numbers to the highest on you got and then pray that everything plays nice with each other.

I use the Microsoft.aspnetcore.signalR package in a .net 4.6.1 project and you have the same issues.

sommmen
  • 6,570
  • 2
  • 30
  • 51
  • I have updated my answer to include your link for complecity as advised by the OP. – panoskarajohn Jan 14 '20 at 09:40
  • Yep. I actually spent maybe 3 days or so struggling with this issue, especially because we're at a point in time where i need to support vs2015 and vs2019. I actually have a class library which runs a script that creates a {name}.exe.config file to work with all the binding redirects. This then gets copied to the executable project (and we pray all versions are backwards compatible). I Hope we can at some point in time upgrade everything to .net std and core. Congrats on the well-deserved bounty, and hopefully we all learned a lot from this endeavor :) – sommmen Jan 14 '20 at 14:47
  • Thanks.I certainly did learn a lot.Feel free to edit my answer and append to it :) – panoskarajohn Jan 14 '20 at 14:51