Skip to content
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

2.0.0-rc.1 Targeting .NETStandard 2.0 doesn't allow upgrading of v5 of framework libraries #390

Closed
Pete-PlaytimeSolutions opened this issue Nov 17, 2020 · 17 comments
Milestone

Comments

@Pete-PlaytimeSolutions
Copy link

.NETStandard 2.0 dependencies do not allow upgrading beyond V 3.0.0

e.g.
Microsoft.Extensions.Caching.Abstractions 5.0.0 is disallowed, even though this version of the library should be available for .NETStandard 2.0

Expected behavior
Should be allowed to upgrade dependent packages like Microsoft.Extensions.Caching.Abstractions to version 5.0.0

@jzabroski
Copy link
Collaborator

jzabroski commented Nov 17, 2020

Hi,
If I understand your point,
https://github.com/toddams/RazorLight/blob/master/src/RazorLight/RazorLight.csproj#L21-L33

disallows the following project structure:

[net5.0 TFM csproj] (executable)
 |
 |-- [netstandard2.0 TFM csproj] (library)
    |-- package references
       |-- RazorLight-2.0.0-rc.1
       |-- Microsoft.Extensions.Caching.Abstractions 5.0.0

as well as

[net5.0 TFM csproj] (executable)
 |
 |-- [netstandard2.0 TFM csproj] (library)
    |-- package references
       |-- RazorLight-2.0.0-rc.1
 |-- package references
    |-- Microsoft.Extensions.Caching.Abstractions 5.0.0

because Nuget will detect a version conflict in that RazorLight caps the version number on dependencies at at UP TO 3.0

To which I would ask - can you switch the netstandard2.0 csproj to use net5.0?

@Pete-PlaytimeSolutions
Copy link
Author

Ideally, I need my csproj to be able to target both netstandard2.0 and net5.0,
otherwise I cannot upgrade any of my other 50 odd libraries to use v 5.0.0.0 of the Microsoft.Extensions.* libraries, or have an incomplete set...

@jzabroski
Copy link
Collaborator

jzabroski commented Nov 17, 2020

Why can't you use <TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks> and then add conditions downstream?

Can you explain why you need to target netstandard2.0? I just don't know every combination people deploy RazorLight with and its these unexpected combinations that are extremely hard to regression test against.


BELOW MIGHT BE AN INCORRECT ANALYSIS ON MY PART. IT IS MY ATTEMPT TO TRY TO OUTLINE BEST PRACTICES FOR MANAGING DEPENDENCIES USING THE NEW CSPROJ SDK FILE FORMAT

To elaborate a bit, using netstandard2.0 at all may have been a mistake with good intentions. The original theory behind netstandard2.0 was to be able to write your code once against a common API and then have common compatibility moving forward with all "in box" .NET APIs as well as a few "out of box" APIs. However, the problem with this is that RazorLight doesn't depend just on a set of APIs but on a FrameworkReference and those FrameworkReference dependencies have their own minimum and maximum support capabilities baked into the IL instructions (they expect certain function calls to be there, etc.).

The good intentions was to give someone the opportunity to target netstandard2.0 if they had minimal dependencies. But in practice, everyone is including things that are transitively included by the FrameworkReference, so you end up in a situation where you get conflicts.

@jzabroski
Copy link
Collaborator

jzabroski commented Nov 17, 2020

I think I understand your problem.

You have this:

[netstandard2.0 TFM csproj] (library)
    |-- package references
       |-- RazorLight-2.0.0-rc.1
       |-- 50 other nuget packages TRANSITIVELY targeting netstandard2.0 5.0.0 libraries like Microsoft.* whatever
       |-- Microsoft.Extensions.Caching.Abstractions 5.0.0

And you have interdependencies between RazorLight and those other packages (e.g., tag helpers, whatever). So it's non trivial to segregate RazorLight from everything else and put it into a net5.0 library so that the late binding of dependencies agrees on/unifies to a common nuget package version.

Is that correct?

@jzabroski
Copy link
Collaborator

Can you share the csproj. I really just need the TargetFramework(s) and PackageReferences.

@Pete-PlaytimeSolutions
Copy link
Author

Pete-PlaytimeSolutions commented Nov 17, 2020

Targeting both is what I am doing, across a suite of about 50 different projects, that roll out as a set.
By being restricted to UP TO V 3.0 by including RazorLight-2.0.0-rc.1, it prevents me being able to include the single Communications library, as part of the rest of my suite of packages.

If I am understanding correctly, the RazorLight package actually requires a FrameworkReference.
If that is the case, that's fine, I can work around that.

I'll just change my communications package to target that various frameworks, rather than the more generic netstandard2.0...
I was just hoping to be able to have the flexibility to chop and change a bit more.. ;)

PS I think its a great library!

@jzabroski
Copy link
Collaborator

@Pete-PlaytimeSolutions I tried to figure out how to make it as generic as possible, but it's quite complicated. I ran into a bunch of unexpected things, and have been trying to keep track of a list of potholes to keep my head straight . My above description of what I think is happening I believe to be correct, but I also don't have the precise info as to why readily available (I can guess at why, but would need to build precise repros to demonstrate counterexamples where a configuration of dependencies you'd expect to work, does not). RazorLight is easily the most complicated dependency I have in all my libraries. That's also why I want to add CefSharp to build out some integration tests to verify some complex dependencies scenarios.

See: jzabroski/Home#5 for my hobby horse of trying to better understand how to support dependencies in .NET better. Feedback welcome.

@Pete-PlaytimeSolutions
Copy link
Author

Pete-PlaytimeSolutions commented Nov 18, 2020

Maybe just a looser version range for the generic netstandard2.0 target, (i.e. without specifying the max version)
could provide a more flexible setup... The max version restriction, is probably only relevant when targeting a specific FrameworkReferences...

e.g.
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <PackageReference Include="Microsoft.CodeAnalysis.Razor" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Html.Abstractions" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.Extensions" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="2.1.0" /> <PackageReference Include="Microsoft.AspNetCore.Razor.Runtime" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.Primitives" Version="2.1.0" /> <PackageReference Include="System.Buffers" Version="4.5.0" /> </ItemGroup>

That way any class libraries using RazorLight, really only care about the minimum version requirement.
Only once the class library is then included in a hosting project (that specifies the framework version being targeted), is the version range enforced....

@Vincentvwal
Copy link
Contributor

Vincentvwal commented Nov 18, 2020

We are currently also running into this issue, When running the older beta versions it works, however we also use a personal shared nuget package which includes Microsoft.Extensions.Caching.Memory 3.1.1

So including this into our .net standard project currently this does not work.
Personally i would also like for .net standard to only use the minimal version but not the maximum.

@jzabroski
Copy link
Collaborator

We can give it a shot and see whether it surfaces any issues

@jzabroski
Copy link
Collaborator

@Vincentvwal for my knowledge, what target framework(s) consume your netstandard2.0 csproj

@jzabroski
Copy link
Collaborator

That way any class libraries using RazorLight, really only care about the minimum version requirement.
Only once the class library is then included in a hosting project (that specifies the framework version being targeted), is the version range enforced....

I think it depends on how and whether a FrameworkReference places an implicit upper boundary on major versions. I am curious and will use dotPeek to see what the output is

We will see.

@Vincentvwal
Copy link
Contributor

Vincentvwal commented Nov 18, 2020

We mainly use .net Core 3.1 as consumers, however our project also consists or a public package (.net standard 2.0) which gets included in .net Framework 4.6.1. this public package does not contain the RazorLight package.

Our full references in out templating project are currently (not working due to packages from the rc.1)
<ItemGroup> <PackageReference Include="REDACTED.Utilities.core" Version="1.1.7642" /> <PackageReference Include="REDACTED.Utilities.Public" Version="1.1.7642" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="2.2.0" /> <PackageReference Include="RazorLight" Version="2.0.0-rc.1" /> </ItemGroup>

Our Utilities.Public contains the following

<ItemGroup> <PackageReference Include="Azure.Storage.Blobs" Version="12.4.4" /> <PackageReference Include="Azure.Storage.Queues" Version="12.3.2" /> <PackageReference Include="Markdig" Version="0.20.0" /> <PackageReference Include="Microsoft.Azure.Storage.Blob" Version="11.1.7" /> <PackageReference Include="Microsoft.Azure.Storage.Common" Version="11.1.7" /> <PackageReference Include="Microsoft.Azure.Storage.Queue" Version="11.1.7" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="NLog" Version="4.7.0" /> <PackageReference Include="NServiceBus" Version="7.2.3" /> <PackageReference Include="System.ComponentModel.Annotations" Version="4.7.0" /> </ItemGroup>

And for us Microsoft.Extensions.Caching.Memory is causing the issue.

@jzabroski
Copy link
Collaborator

jzabroski commented Nov 18, 2020 via email

@jzabroski
Copy link
Collaborator

Pushed 2.0.0-rc.2

@jzabroski jzabroski added this to the 2.0.0-rc.2 milestone Nov 18, 2020
@jzabroski
Copy link
Collaborator

Some final thoughts:

  1. FrameworkReference is fairly confusing, since they don't show up on nuget.org (I just realized this now).
  2. When you're in a netcoreapp3.1 context, netstandard2.0 should bind to Microsoft.Extensions.* packages in the [3.1,4) version range, because RollForward is Minor and you're referencing a FrameworkReference to Microsoft.AspNetCore.App which will resolve to 3.1 right now (I believe this is correct). This would also be why the current release broke, because it broke this flow of dependencies.
  3. When you're in a net5.0 context, netstandard2.0 should bind to Microsoft.Extensions.* packages in the [5.0,6) version range, because RollForward default is Minor and you're referencing a FrameworkReference to Microsoft.AspNetCore.App which will resolve to 5.0 right now.

However, the problem remains that you can run into runtime late binding issues if netstandard2.0 libraries deprecate some logic due to the fact we have pre-processor pragmas to handle higher framework versions. So, extra care has to be taken when defining these pragmas, as its the "Error of omission" that likely causes most run-time late binding errors.

@Pete-PlaytimeSolutions
Copy link
Author

Thanks John, 2.0.0-rc.2 is working a treat!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants