-
Notifications
You must be signed in to change notification settings - Fork 118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] .NET8 support (as well as previous versions) is probably broken starting from v2 #377
Comments
Hi @andriibratanin , the version of Microsoft packages is not (necessarily) related to the corresponding .NET version. You can use a main package v9 with .NET 8 or 7. For example: the package Can you give me more info about your dependencies? |
I agree that versioning discussions are very old: dotnet/extensions#2700 (comment), but I believe the major version should not change Here is my csproj file header:
Here are my dependencies:
Please note I use CPM (central package management), so the notation is a little bit different ( NU1605 errors are not trivial to investigate. For example, the original error message was:
but it has nothing to deal with We will be able to see the correct error message only after explicitly setting package versions:
finally resulting in:
which seems like a dead-end without "partially upgrading" to .NET9 PS: explicitly sticking .NET version with
|
This is happening to me too (same situation exactly). I can't upgrade to v2. |
I'm reading about this online to get a better grasp of the best practices (thanks for the links!) So basically the idea would be to specify, for each TFM in the multi-target list, the package version related to that TFM? So if I have multi-target with .NET 8 and 9, I should specify the dependency on And what about .NET Standard 2.0, what should I do there? Also, wouldn't this create confusion for users? I'll take a look at existing Microsoft packages to see if they actually do the same. I'm trying to understand how this would work with all the edge cases, so any help in exploring the scenarios is appreciated, thanks! Regarding your specific: case I think you have a direct dependency on one of those packages with a specific version, for example you probably have a direct dependency on |
I'll add: looking for example at the Microsoft.Extensions.Caching.Hybrid package, they ( @mgravell basically) are doing the same thing there: Another example is System.Text.Json, and they do the same: Maybe is the direct reference I mentioned? Or maybe something specific about CPM (central package management)? I'll keep looking into this. |
No, I have no dependency on Let me help you to figure how it works Just create an empty console application or worker service targeting .NET8 and then experiment with installing the following sets of packages (no code changes are required at all): Case 1: Only FusionCache is installed: <!-- Case 1: Only FusionCache is installed - builds ok -->
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1"/>
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.1.0" />
<PackageReference Include="ZiggyCreatures.FusionCache.Serialization.SystemTextJson" Version="2.1.0" /> this builds ok Case 2: FusionCache + DependencyInjection.Abstrations: <!-- Case 2: FusionCache + DependencyInjection.Abstrations - build fails -->
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1"/>
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.1.0" />
<PackageReference Include="ZiggyCreatures.FusionCache.Serialization.SystemTextJson" Version="2.1.0" /> this will not build! Now, interesting ones, my counter-arguments: Case 3: MemoryCache v8 + DependencyInjection.Abstrations (no FusionCache) <!-- Case 3: MemoryCache v8 + DependencyInjection.Abstrations - builds ok -->
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1"/> this builds ok! BUT, as soon as we upgrade the version of MemoryCache to 9 everything breaks: Case 4: MemoryCache v9 + DependencyInjection.Abstrations: <!-- Case 4: MemoryCache v9 + DependencyInjection.Abstrations - build fails -->
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1"/> build fails. So, unfortunatelly, we are mistaken supposing that stated support of version below 9 is true when we are not actually on .NET9 Extensions packages are using Central Package Management with conditionals:
I suppose they run build multiple times (to publish packages for v8, v9, etc.) |
I'm also hit by this so I cannot upgrade, since we are in the LTS version. I don't really know about packaging and versioning, but as an example, another package I'm using (Grpc.Net.Client) seems to depend on the minimum needed version they need, instead of the latest: Here is the csproj file. There is this
That variable is defined in a props file. |
Just for reference: here is one more issue reported in Extensions repo regarding v9 packages usage in v8 applications: |
Hi all, thanks a lot for all the infos! I'm trying to understand the best course of action to ease the update for you, without introducing some breaking changes all the others who already updated to v2. Currently I'm thinking about a couple of options, like:
Currently though I'm not sure which one (or another one) would be the best path. I'll keep you updated, thanks again for the discussion and the sharing of info. |
Can OP try to upgrade all direct references of Also, why are you locking to the .Net 8 SDK? The SDK itself should be the latest always. The oldest SDK you can need today is .Net 6, but that's because 9 dropped support for building 6. If you develop for .Net 8 today, you need two things. 9 SDK and 8 runtime. This is not relevant to the issue, I'm just curious what is the justification. |
If that is the case, why force depend on the latest version instead of the minimum version needed? If there are no runtime blockers, older versions should work. In any case, I disagree. In my opinion, 9.* packages are for .NET 9, and 8.* packages are for .NET 8. Take this one, for example (although any other AspNetCore would do). Versions 8.0.11 and 9.0.0 were released 3 months ago. Versions 8.0.12 and 9.0.1 were both released 24 days ago, meaning that the 8.* branch is still maintained. If it was only convenience, they wouldn't have bothered with 8.0.12.
.NET 8 is LTS, .NET 9 isn't. That is the justification in my case (and probably in most of the people who are still with .NET 8). As per above, .NET 8 is still actively supported (as expected in a LTS version), so I will keep using it until .NET 10 arrives. @jodydonetti I have found some other packages which depends on the "correct" Microsoft packages versions: That is from NSwag.AspNetCore. Here are the csproj and the nuspec, as you can see, for |
Help me understand one detail please: your requirements are to "reference The thing I'm not understanding is why it's not possible to update those references to the related v9 versions, since they are all backward compatible and would not create any issue (at least to the best of my knowledge, in case that's not the case let me know). As an additional info: I know some are using FusionCache v2 on the very old .NET Framework v4.X, and even they don't have any problem at all, so I'm trying to understand where exactly is the problem, so I may get to a solution. |
For clarity on .NET major releases:
|
As for package versioning, I think we should look at other community packages instead. So it is actually quite common to depend on the latest major version of a package, even if you multi TFM to older versions. |
As a counter point take NLog for example they tried to do what OP wishes, but it has a very unintended side effect of .net 9 falling back to netstandard2.0 and using the ancient |
Believe .NET9 will fallback to closest target-framework which is NET8. Just like NET48-project will fallback to NET461. When I compile a NET9-project that depend on |
I think we should look at the ones I posted.
Serilog replicates the .NET versioning schema exactly for these reason. Users of the .NET 8 should use the 8.* packages, as explained here.
Opentelemetry needs the latest versions of System.Diagnostics, but in any case, the decision of bumping all the dependencies to the latest version was also somewhat controversial
Considering that .NET 8 is LTS, that's not as big of a problem. And in any case, they don't force STS packages to the LTS users.
That is irrelevant. Microsoft offers LTS versions for a reason. Some companies mandate the use of LTS. In any case it seems that nuget guidances in this respect, so the library authors should depend on versions above the TFM ONLY if they need it, so this topic should be settled: do not force upgrades if it's not needed. |
That last link seems to be the answer then. It is just Nuget hell. I personally had to make this decision once or twice too. I justified it by Microsoft saying the quality of releases is the same. Which I still stand by, the whole LTS thing is very silly, 3 years is not long term, (besides the point), but I have unfortunately encountered game breaking bugs that I reported to the core repo, and for each report the fix only arrived in the next major version. This was very dissappointing, so maybe that is my bias for not caring about LTS as to me it literally was meaningless since my bugs were left unfixed on the current version. It does beg the next question though. What does this guidance mean to community libraries? Suddenly the support matrix for multi TFM libraries have increased quadratically. You now not only have to fight the runtime provided APIs, like Or the worst case scenario (I hope this isn't the case) this basically forces every single community library to adopt TFM specific support/versioning. Also this is very interesting, the 8.x platform extensions were out of support last year already. |
This weekend I'll try to get to the bottom of this. One thing to note about this specific case is that the |
I've been wondering when this would come up
…On Fri, Feb 7, 2025 at 6:05 PM Jody Donetti ***@***.***> wrote:
This weekend I'll try to get to the bottom of this.
One thing to note about this specific case is that the HybridCache
abstractions are there only in the v9 of the
Microsoft.Extensions.Caching.Abstractions package, and they are needed in
v2 because of this
<https://github.com/ZiggyCreatures/FusionCache/blob/main/docs/MicrosoftHybridCache.md>
.
—
Reply to this email directly, view it on GitHub
<#377 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAAPCR56SGDUI5PSFQHHOIT2OVC6JAVCNFSM6AAAAABWPUOA3CVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMNBUGM2TEMBYGA>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
For me, "just upgrade" sounds strange, because it cancels the idea of versioning at all:
Upgrades are actually not as simple as we try to imagine them - they take time: to make the decision, to read release notes, to re-compile. For example, do you know that "FluentAssertions" are no longer free starting from their version 8? "Just upgrade" policy may lead you to very unpleasant consequences one day... Personally, it is not hard to check-out the repo and re-compile it targeting v8 - kudos to Jody and the community - the code is covered with tests very good. The fun fact is I am 99% sure it will compile and will not have any issues... So, yes:
Happy have started this topic - we've already learned alot and, probably, gave another nudge to MS for clear guidances... ;) |
Backporting patch for those who can't wait to play with such a beauty as v2: Only HybridCache was removed, Tags are still there. |
With all due respect @andriibratanin, you upgraded to a major version of FusionCache, and experienced an incompatible change that you disagree with. And to reiterate it is NOT an incompatible change, because the package still works on |
From the very first post:
They are not multi-TFM, it is just a copy-paste in release notes. Please consider doing the experiment yourself from this post: |
Ok fellas, now that my weekly daily job + commute is off the table, I'm reading all the backlog of comments, links and whatnot, here and on social. First thing that came up from a comment on Bluesky is this video, where at the 24m40s mark @DamianEdwards starts by saying "ok, this is an incredibly complicated question..." So yeah, will watch some videos, read some docs and csproj files posted here and will get back to you all with my thoughts. Wish me luck. |
I did the experiment, this would be my solution: Case 2 fixed: <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- Case 2: FusionCache + DependencyInjection.Abstrations - build fixed -->
- <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1"/>
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.1.0" />
<PackageReference Include="ZiggyCreatures.FusionCache.Serialization.SystemTextJson" Version="2.1.0" />
</ItemGroup>
</Project> Case 4 fixed: <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- Case 4: MemoryCache v9 + DependencyInjection.Abstrations - build fixed -->
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.1" />
- <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1"/>
</ItemGroup>
</Project> |
Ok so I read the things and watched the videos, thought about it both in general and in the particular case of FusionCache. I'll now try to summarize the main points about this kerfuffle, step by step in a logical order, trying to stick to the things that are strictly related here to avoid confusion with secondary stuff. Note I may have missed something or made mistake in my analysis. In that case please let me know, ideally with some useful links to better understand the specific detail, thanks. Here we go. What we mean when we say ".NET"Seemingly silly to specify this, but just to be all on the same page about the details and their nuance. So .NET as a whole is a product, composed of multiple things, and the things that are relevant to this discussion are:
Each .NET "main version" like 7, 8 and 9 corresponds NOT ONLY to the runtime, but ALSO to the related core packages that comes out with tha .NET product: for example, when .NET v9 came out, it was accompanied with v9 core packages. This does NOT (necessarily) mean that each v9 core package requires v9 of the .NET runtime: since core packages are "just" packages, they are frequently backward compatible with older TFMs. A TFM (Target Framework Moniker) is a string used to identify a version of .NET "product" (meaning, as a whole).
Package versionsNow anyway, by targeting the .NET 8 TFM we basically get a dependency on all the related core packages from that version, but we can also reference those same packages (even in the same version) directly. For example, As we can see when targeting .NET Standard 2.0 there are a lot of packages referenced, including The main (but not necessarily the only) reason for multi-targeting a library is to have less dependencies declared in the manifest (even though they are there anyway at runtime because they are part of that runtime version itself). Support Policy: STS/LTSEach major .NET product version (so runtime + core packages) coming out in November each year has a corresponding support policy, either STS or LTS: an STS version is supported for 18 months, while an LTS is supported for 36 months (3 years). For various reasons (eg: primarily costs associated with long term support of multiple versions) Microsoft decided that each major version of .NET would alternate between STS and LTS, so for example .NET 6 is LTS, .NET 7 is STS, .NET 8 is LTS and the recently released .NET 9 is STS. Important Now, this is important: the product quality, support quality , documentation and everything else for both an STS and an LTS version is exactly the same. Like totally, 100%. It just happens that the support duration on an STS is 18 months, which btw crosses over the next version (after 12 months) which will be LTS: this means that using an STS will give us 6 months after a new LTS version comes out to move to it. One thing to pay attention to is that the support policy is for the entire product: so NOT ONLY for the .NET runtime, but also for the core packages. This means that companies that, for whatever reason, only allows their tech people to use an LTS version, are forcing them to stay on an older tech stack: again, not just the runtime, but also core libs/packages, and so on. And you'd be surprised to know how many times companies don't even know that even by disallowing, for example, .NET 9 they are not in fact actually blocking all the v9 core packages, and they'll get updated anyway without nobody even noticing. Package Version LiftingNow let's say we create a console app for .NET 8: this means we are implicitly referencing the v8 core packages, right? An automatic "lift" happens, and now our console app is referencing v9 of that package, along with all its dependencies (probably v9 themselves). The good @DamianEdwards clearly explains all of this in a video, where at the 24m40s mark starts with "ok, this is an incredibly complicated question...": I think his explanation is complex (because the thing itself is complex, and cannot simplified more) but masterfully clear in the way it's explained. Really, go watch it, it's that good. So anyway, can the automatic lift always work? No, there's one case where this process breaks, and it's when our app has a direct dependency to the same package. Basically, if for example in our app we directly reference v8 of In cases like this we'll receive an error like So, what can we do? We need to either:
So, what should library authors do? Library authors: what versions to target?Now, library authors like myself need to decide what to target: apart from multi-targeting or not, we need to pick a baseline minimum version. Of course we can target whatever minimum version we want: let's keep in mind it's our own project, our choice, etc. But. But let's see what we may do to give good support to people and companies that (good or bad that it is, and I personally think it's at least very limiting) want to stick to LTS only versions. While doing so, let's also say that a reasonable thing in this regard is to support only the last LTS (right now this means .NET 8): everything more than that (eg: also target .NET 6 or below) is of course possible, but let's say not strictly "necessary". I can see 2 different options:
Oh, and in the 2nd option above I said I "highest version" because instead of sticking to the baseline Anyway with both options users would still be able to directly upgrade core package references to v9 or more, and everything should (in theory) work correctly, so there's also freedom there. But what if v9 core packages have new overloads that are more optimized? We library authors may have optimized code-paths via some And what if instead v9 core packages have totally new stuff, with new types to use and maybe return from our public methods? It becomes harder to opt-in to those things via Now, unless v9 core packages have some new features strictly needed by the library, "Last LTS" can be the reasonable thing to do, imho. Ok so, can I do this for FusionCache v2? Eh... Ok so, FusionCache v2?In theory I may easily change the references to v8 core packages for FusionCache, and in fact this is what I basically ended up doing in the past (meaning: generally sticking to the latest LTS), except... this time I can't, because of this feature. You see, the HybridCache abstractions (eg: If I try to get back to v8 core packages the project would not compile, that's it. The 2nd option above is also not an option, because by multi-targeting .NET 8 with v8 core packages and .NET 9 with v9 core packages, it would not compile for .NET 8 for the reasons above. Sure, I can go with Case in point, currently I have about 27K downloads of Another option I'm thinking about is that, in theory, I can stick to v8 core packages for the main package and create a new package that contains just the HybridCache adapter, where I can reference v9 packages. Want the HybridCache-compatible stuff? Use the extra package, but with v9 core packages. Don't want that? Use the main package only so that, if you want, you can stick to v8 core packages. I even already have such package, see here: Before going GA with v2 though, the extra package became "useless" and I emptied+deprecated it, see here for why. The problem in doing the extra package thing anyway is that this would be a breaking change, and everybody already happily using FusionCache v2 will then update and have problems. On the other hand, I can simply stay like it is right now: user wanting the new stuff can update their core packages, or stay with the v1.4.1 version. The problem is that v2, apart from the HybridCache thing, contains A LOT of wonderful stuff like Tagging and Clear support, which are honestly huge. And I'd really really like to give those features to the widest possible group of users 🥲 Oh, also: I'm working on adding support for the new (optional) Conclusions (actually, not yet)So anyway, I'm still thinking about what to do here, pondering pros and cons. Meanwhile, please let me know what you think: did I miss something? Did I say something wrong? I'd like to know before making a decision. Will update as soon as I get to a definitive answer. Thanks! PS: all of this will become basically useless as soon as .NET 10 (LTS) will be out in November. Yeah, I know, 9 months, but still. |
I understand your points and priorities, so agree that it is better not to fix the "issue" in favor to move on and be first. Personally, I've successfully re-built v2 without HybridCache for experiments - the link to the patch is above. Now, without regard to FusionCache and without intent to offend anyone. Lessons learned for building DLLs:
Jody, I reassure you that no matter what your decision will be, our support and gratitude for your efforts will not become less. Thank you very much for FusionCache! |
Why don't you use range version on your dependencies. Unless you are using any specifics of .net if you reference the dependency as Version="[8.0.0,)" should suffice. Then who install your package already decides the TFM. |
Hi @dariogriffo
Well in general when you say Also nope, I can't specify 8 as the minimum version, because of this:
|
Right now I have not settled for a definitive decision yet, I'm still thinking of a possible way out that would make everyone happy.
This is something I care to clarify. But again I really really care to say that this is NOT what I did: you are probably referring to the fact that "HybridCache is not out yet" and I came out first, but this is not what I did in this context. Let me explain. The "default HybridCache implementation from Microsoft" is not out yet, true, but FusionCache doesn't need that. FusionCache needs the "HybridCache abstractions" (aka: the abstract class and support options) and those came out officially as GA with .NET 9 in November, they are fixed in stone and cannot change, at all, at least until .NET 10. The only thing they may change in an hypothetical .NET 9.1 is some xml docs and stuff like that, which would not affect FusionCache at all. I could have rushed to "be first" by coming out immediately in November after the .NET 9 release since all the pieces were basically already there, BUT I consciously decided to instead wait (risking not to "be first") and to do things well, because that is the most important thing to me. Be first? Nice, but definitely NOT important. All of these words just to say that this is not my modus operandi.
Don't worry, no offense taken, at least on my side 🙂
Totally agree! Also, if the new things some lib author is doing are still "experimental", there's an attribute for that (btw originally proposed by yours truly 😬). Just want to point out that in this specific case I did not "rely on things still not in production", as explained above.
Thanks: I know that is a delicate topic with a lot of ramifications, so I really really appreciate it. |
First and foremost, thank you @jodydonetti for your outstanding work. I’ve been following this closely as I’m dealing with the same conundrum as a framework author. Just my two cents on the matter - unrelated to FusionCache. Our in-house framework has several dependencies on libraries like MySql.Data, MySqlConnector, MongoDb, SQL Server Client, npgsql, Kafka, OpenTelemetry and of course Microsoft’s Platform Extensions. I’m sure I missed some. No, I definitely missed some - the expanded dependency tree is bound to surprise me if I were to examine each package recursively. It’s becoming borderline impossible to match the TFM’s major version with an arbitrarily complex dependency tree. I’ve been trying to abide by the “major versions must match” rule but I’m seeing a general ecosystem shift towards whatever new version is out for updates. Unsurprisingly, this implies a non-zero probability of introducing breaking changes that creep up transitively; for example, some Microsoft Platform Extension (can’t remember which off the top of my head) had a different nullability annotation that caused a compilation error. It’s nothing to write home about, I can fix that for my own codebase - but I can’t do the same for consumers of my framework, which will get a breaking change for free, as it were. Long story short, this has a real implication on how we approach versioning as library and framework authors. In all honesty, I wish Microsoft kept the Platform Extension updates in line with the TFM, so that the whole ecosystem would follow suit. However, I’m afraid that particular ship has sailed, and whatever the decision, some are bound to be disappointed. I for one will attempt to introduce dependencies with versions >= TFM since I believe I’ll be forced to in the short term. Again, just my 2c - thank you all for the great comments so far. |
Thanks @rcollina for sharing you experience here, it adds to the mix of knowledge to try and get to some overall guidance, all things considered. And yes: when implemented, the compatibility matrix with multiple TFMs can become quite crazy, quite fast 🥲 Again, really appreciate it. |
I stumbled upon NU1605 error (package downgrade detected) in my project (which targets .NET8 SDK) after installing FusionCache v2.
Checking https://github.com/ZiggyCreatures/FusionCache/blob/main/src/ZiggyCreatures.FusionCache/ZiggyCreatures.FusionCache.csproj:
but finding also:
Reason:
Version 9 of "Microsoft.Extensions.Caching.Memory" NuGet package (as well as others) is causing NU1603 error in projects targeting .NET8 SDK (and probably 7, 6, etc.).
Arguments:
The text was updated successfully, but these errors were encountered: