dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.77k stars 3.18k forks source link

Installing EF Core on a .Net Core 3.1 Project? #26872

Closed mlhpdx closed 2 years ago

mlhpdx commented 2 years ago

Is it expected and intentional that installing efcore on .Net Core 3.1 project has been broken since the release of efcore version 6?

Steps to reproduce

> mkdir new-project && cd new-project && dotnet new console -f netcoreapp3.1 && dotnet add package Microsoft.EntityFrameworkCore.SqlServer
The template "Console Application" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on C:\Users\lhard\source\repos\new-project\new-project.csproj...
  Determining projects to restore...
  Restored C:\Users\lhard\source\repos\new-project\new-project.csproj (in 50 ms).
Restore succeeded.

  Determining projects to restore...
  Writing C:\Users\lhard\AppData\Local\Temp\tmpBC20.tmp
info : Adding PackageReference for package 'Microsoft.EntityFrameworkCore.SqlServer' into project '...\new-project\new-project.csproj'.
info :   CACHE https://api.nuget.org/v3/registration5-gz-semver2/microsoft.entityframeworkcore.sqlserver/index.json
info :   GET https://nugetized.blob.core.windows.net/skiasharp-eap/registration/microsoft.entityframeworkcore.sqlserver/index.json
info :   NotFound https://nugetized.blob.core.windows.net/skiasharp-eap/registration/microsoft.entityframeworkcore.sqlserver/index.json 502ms
info : Restoring packages for ...\new-project\new-project.csproj...
error: NU1202: Package Microsoft.EntityFrameworkCore.SqlServer 6.0.0 is not compatible with netcoreapp3.1 (.NETCoreApp,Version=v3.1). Package Microsoft.EntityFrameworkCore.SqlServer 6.0.0 supports: net6.0 (.NETCoreApp,Version=v6.0)
error: Package 'Microsoft.EntityFrameworkCore.SqlServer' is incompatible with 'all' frameworks in project 'C:\Users\lhard\source\repos\new-project\new-project.csproj'.

FWIW, I have the .Net 3.1, 5 and 6 SDKs installed:

> dotnet --list-sdks
3.1.415 [C:\Program Files\dotnet\sdk]
5.0.403 [C:\Program Files\dotnet\sdk]
6.0.100 [C:\Program Files\dotnet\sdk]

I've checked https://docs.microsoft.com/en-us/ef/core/get-started/overview/install and the above package installation command is what's recommended, but it doesn't specify which targeted .Net Core versions that should work with.

I've checked https://docs.microsoft.com/en-us/ef/core/miscellaneous/platforms and it seems that version 5 is still supported, but perhaps that's incorrect since it also lacks mention of version 6 entirely.

mlhpdx commented 2 years ago

It probably bears mentioning that this issue also makes for trouble using nukeeper to update packages on a .Net Core 3.1 project since it stops on the same error updating the EF Core package.

roji commented 2 years ago

As the error message says, EF Core 6.0 requires .NET 6.0 and is not compatible with netcoreapp3.1. You can use EF Core 5.0 or 3.1 by passing the -v switch to dotnet add package. Note that what matters isn't which dotnet SDKs you have installed on your system (dotnet --list-sdks), but which target framework you're specifying in your csproj.

lee-11 commented 2 years ago

You can use EF Core 5.0 or 3.1 by passing the -v switch to dotnet add package.

Yes, of course I can tediously manually edit the version in the .csproj instead of relying on nukeeper and having the confidence I'm applying all the patches to my packages in a timely manner, too. That seems very 1999 to me, though, and isn't something I need to do for any other package.

Why is this a problem for EF Core 6 when it wasn't a problem for 5?

roji commented 2 years ago

@lee-11 I'm not familiar with nukeeper, but your cmdline above doesn't show you using it - dotnet add package Microsoft.EntityFrameworkCore.SqlServer is part of the dotnet CLI and not nukeeper. If nukeeper is proposing an EF Core version that is incompatible with your project's TFM, that would be an issue take up with them.

EF Core 6 requires the net6.0 TFM in order to take advantage of new .NET features; continuing to support netcoreapp3.1 would mean holding EF Core back in various ways. If you want the latest EF Core, you're going to have to use the latest .NET as well. Since both .NET 6.0 and EF Core 6.0 are long-term support releases (LTS), it's a good idea to do the upgrade.

lee-11 commented 2 years ago

Here are some things for the EF Core team to think about.

This question isn't about nukeeper. I happen to use it, but there are many ways to keep packages up to date automatically. Nukeeper just wraps dotnet add package and automatically makes PRs with package updates. Kind of magical. You should check it out.

roji commented 2 years ago

Making the established user base suffer because the team chose to be on the leading edge of .Net 6 adoption is, in my opinion, a little toxic. I don't have the option to upgrade these projects to .Net 6. Hundreds of them.

This is an interesting statement that I'd personally like to understand better. You seem to be wanting to upgrade these (hundreds!) of projects to EF Core 6.0, but see the required .NET 6.0 dependency as problematic. For one thing, .NET versions are generally easier to upgrade - .NET is at the bottom of the stack, and the bar for breaking changes is extremely high; whereas in EF Core we probably introduce breaking changes a bit more frequently when we're convinced it serves the interests of our user base.

In other words, it's totally fine for you to keep applications using older versions of .NET and EF Core; but it's a bit odd to expect to be able to upgrade to the latest EF Core versions while staying on older .NET versions. The price for that is holding back everyone else which is using newer .NET versions, since EF Core wouldn't be able to use/support those new features.

It's possible to package multiple versions targeting different frameworks in the same package. If there will be no further patches to EF Core 3.1 and 5 that should be made extremely clear they are no longer supported so people understand that using them presents a compliance (and practical security) issue. If they will be patched, bundle them in the package targeting the appropriate framework.

It isn't clear to me what you're saying here. The support policy for EF Core is published in a very explicit way on our releases doc page, along with clear support expiration dates etc. As listed there, EF Core 3.1 will be supported until December 2022 (as an LTS version).

More importantly, it's crucial to distinguish EF Core versions and .NET versions. You seem to be suggesting that we bundle EF Core 5.0 - which still supports netcoreapp3.1 - in the same nuget as EF Core 6.0, effectively mixing different EF Core versions in the same nupkg. This doesn't make much sense; apart from the question of what version this nuget would have (5.0? 6.0?), it would mean that changing TFMs in your project suddenly selects a completely different version of EF Core, with (possibly breaking) behavior changes. This simply isn't an acceptable way to manage package versioning.

At the end of the day, it's your responsibility to specify dependencies (and dependency versions!) which are compatible with your project (e.g. TFM). I'm well aware of automatic package updates, and use github's dependabot regularly (probably somewhat similar to nukeeper). If your automatic package update tool doesn't automatically exclude dependency versions which are incompatible with your TFM, then you should probably be locking/specififying/managing the version manually.

mlhpdx commented 2 years ago

In other words, it's totally fine for you to keep applications using older versions of .NET and EF Core; but it's a bit odd to expect to be able to upgrade to the latest EF Core versions while staying on older .NET versions.

That's not my expectation. My expectation is that when I do a dotnet add package of a package on a project that is targeting .Net Core 3.1 I will get the latest code (I really don't care what the version number says) that is compatible with .Net Core 3.1. When I do the same on a project targeting .Net Core 6 I will get the latest code that is compatible with .Net Core 6. I also expect that if I try to install the package in a project that is based on .Net Core 1.1, and that framework isn't supported, I'll be kindly told no.

It isn't clear to me what you're saying here. The support policy for EF Core is published in a very explicit way on our releases doc page, along with clear support expiration dates etc. As listed there, EF Core 3.1 will be supported until December 2022 (as an LTS version).

Right, so I understand correctly that EF Core 3.1 will continue to get patches, and I'm going to be updating to each and every one as soon as they are available because I trust the authors to fix important things and I won't second guess you.

More importantly, it's crucial to distinguish EF Core versions and .NET versions.

Understood.

You seem to be suggesting that we bundle EF Core 5.0 - which still supports netcoreapp3.1 - in the same nuget as EF Core 6.0, effectively mixing different EF Core versions in the same nupkg. This doesn't make much sense; apart from the question of what version this nuget would have (5.0? 6.0?)...

Yes. As I indicate above, that's not an unusual practice since it allows everyone to get what they need based on the project's target framework. Call the versions what you will, I just want the latest compatible code when I add the package (not the latest code for a different framework I'm not targeting).

... it would mean that changing TFMs in your project suddenly selects a completely different version of EF Core, with (possibly breaking) behavior changes. This simply isn't an acceptable way to manage package versioning.

I disagree. It's simple, and makes it easy for project authors to keep up with patches to supported frameworks. Given the very, very high level of importance in staying up-to-date with patches in the face of the modern onslaught of vulnerability exploits this is a paramount concern for me.

I do recognize it make it a little difficult to say "version 6.1.2 of our package is version 6 of efcore" but, frankly, that's not my problem it's yours.

At the end of the day, it's your responsibility to specify dependencies (and dependency versions!) which are compatible with your project (e.g. TFM).

Exactly what I'm doing, and want to keep automated. Agreed.

I'm well aware of automatic package updates, and use github's dependabot regularly (probably somewhat similar to nukeeper).

I use dependabot, too. It has the same behavior.

image

If your automatic package update tool doesn't automatically exclude dependency versions which are incompatible with your TFM, then you should probably be locking/specififying/managing the version manually.

And there is the problem. My package update tool is dotnet add package and your package breaks it.

roji commented 2 years ago

My expectation is that when I do a dotnet add package of a package on a project that is targeting .Net Core 3.1 I will get the latest code (I really don't care what the version number says) that is compatible with .Net Core 3.1. When I do the same on a project targeting .Net Core 6 I will get the latest code that is compatible with .Net Core 6.

Simply put, that is not how things work. I'll summarize this as succinctly as I can:

I do understand the problem you're describing, and I do think it deserves thought; but I believe the answer should lie in the tools managing dependency updates (e.g. dependabot).

I also expect that if I try to install the package in a project that is based on .Net Core 1.1, and that framework isn't supported, I'll be kindly told no.

That is exactly what happened in your original post above: Package Microsoft.EntityFrameworkCore.SqlServer 6.0.0 is not compatible with netcoreapp3.1.

Right, so I understand correctly that EF Core 3.1 will continue to get patches, and I'm going to be updating to each and every one as soon as they are available because I trust the authors to fix important things and I won't second guess you.

Yes; as long as EF Core 3.1 is supported (i.e. until December 2022), we will continue to release patches to fix critical bugs, security issues etc.

lee-11 commented 2 years ago

Simply put, that is not how things work.

Strange then how what I'm expecting is exactly what's described as the way nuget works:

https://docs.microsoft.com/en-us/nuget/create-packages/supporting-multiple-target-frameworks

Put different framework targeting contents in sibling folders and that's what will be chosen at restore time based on the project's target framework. It's not just possible, it is intended and not uncommon.

In this world you describe where EF core 3.1 is supported and patched, how do you expect people to keep up with those patches? Just periodically check? Maybe write their own script to poll nuget.org and see if there are new versions, then specify that version exactly in order to upgrade to them? Seriously? Do you expect the dependebot and nukeeper authors to smooth it over when it's a package composition problem?

I'm done trying to convince you. Hopefully others pick it up from here because EF Core is an important package, and those using it don't deserve this pain.

roji commented 2 years ago

Strange then how what I'm expecting is exactly what's described as the way nuget works:

You're mis-reading the intended purpose of that mechanism.

It's indeed possible for the same nupkg to contain different assembly versions for different TFMs, and that indeed allows supporting older TFMs where there are slight differences that can be worked around with conditional compilation. For example, a new version of a library may want to provide an API that can only works on net6.0, e.g. because it relies on a type that has only been introduced in that TFM. That API can be compiled conditionally (#if NET6_0_OR_GREATER), and so the nupkg can contain one assembly targeting net6.0 with that API, and another targeting net5.0 without it.

That is worlds apart from including 2 completely different major versions of the same product in the same nupkg - no serious .NET library does this as far as I'm aware. There's a very strong (and IMO justified) expectation for the same nuget version to always contain the same product version.

In this world you describe where EF core 3.1 is supported and patched, how do you expect people to keep up with those patches?

The traditional .NET package management is indeed manual - at some point before a release, you check if your dependencies are up to date and update them (tools such as dotnet outdated can help streamline that, if not automate it). Note that even when dependabot is used, there's still the manual gesture of approving and merging the PRs submitted by the bot; you can simply manually reject dependabot PRs which are incompatible with your TFM (those would fail your CI build anyway). This isn't a perfect solution, but it really doesn't seem like a huge problem either.

I suspect that dependabot was written with scenarios in mind where the project always wants to on the latest dependencies - including with the .NET version used. After all, everything works well as long as you're on the latest TFM. As I wrote above, I do think the problem you're describing exists and should be addressed - it should be possible to use dependabot to update dependencies to the latest version which supports your TFM. That's the right fix here, rather than packing multiple major versions into the same nuget.

I'm done trying to convince you. Hopefully others pick it up from here because EF Core is an important package, and those using it don't deserve this pain.

Once again, understand that you're trying to argue something against the entire .NET ecosystem - this conversation has nothing to do with EF Core. And while mixing versions inside a nuget would work around the specific problem you have - which really doesn't seem all that critical - you seem to be ignoring all the downsides and complications it would introduce.

mlhpdx commented 2 years ago

Welp, we differ. To me any manual intervention in a patching process is simply a non-starter. My commitment to customers is always applying security patches in a timely manner, and that means full automation.

The vast, vast, vast majority of the community (ecosystem) are package consumers. A little difficulty/distaste on the part of package authors is entirely worth it to keeping the package consumer's life simple, and secure by default.

If EF Core 6 was so radically different than 3 or 5 that it couldn't be in the same package, then it should have been a new package. Or, maybe you could have created a package for the 3.1 and 5 versions, and documented that path for "legacy" (but still supported) projects. Either would have been better than the current situation IMHO.

roji commented 2 years ago

Welp, we differ. To me any manual intervention in a patching process is simply a non-starter. My commitment to customers is always applying security patches in a timely manner, and that means full automation.

Have you raised this with dependabot and/or nukeeper? If it's handled on their end, that fixes things for the entire ecosystem, whereas what you're proposing would have to be done for each and every package (EF Core is just one package).

Is there any reason you're insisting that this is an EF Core problem and not a dependabot problem?

A little difficulty/distaste on the part of package authors is entirely worth it to keeping the package consumer's life simple, and secure by default.

You may not have fully thought about the consequences of what you're proposing. If you mix different EF Core versions in the same nuget package, it becomes extremely difficult to answer the question "which version of EF Core am I using". The nuget version becomes meaningless, and the only way to know what's being used is to locate the assembly for the given TFM within the nupkg, open it and examine the [AssemblyVersion] attribute inside, or similar. In fact, what the "given TFM" is isn't trivial either, as your consuming application (or library) may itself be multitargeting - if that happens, your own code may need to interact differently with EF Core, as it's using different major versions when targeting different TFMs.

This is, quite simply, an unworkable state of affairs.

If EF Core 6 was so radically different than 3 or 5 that it couldn't be in the same package, then it should have been a new package. Or, maybe you could have created a package for the 3.1 and 5 versions, and documented that path for "legacy" (but still supported) projects. Either would have been better than the current situation IMHO.

It's easy to make assertions like this, but the reality of the world of software simply doesn't work like that. EF Core is different than 3 or 5; I wouldn't use the word "radically", but these are three major versions which differ behaviorally, and may very well include breaking changes (remember, major versions may contain those). Asking us to have a new package name every time a breaking change is introduced just doesn't make sense.