Search code examples
.net.net-corenuget.net-standardpackagereference

Manage nuget packages with PackageReferences when .Net Framework+Standard+Core combined


Question:

How to manage nuget packages in a VisualStudio2017 solution with 3 platforms:

  • .Net Standard 1.3
  • .Net Framework 4.6
  • .Net Core 1.0

?

I like the PackageReference approach, but i don't know how to use it. They talk about:

<ItemGroup>
    <!-- ... -->
    <PackageReference Include="Contoso.Utility.UsefulStuff" Version="3.6.0" />
    <!-- ... -->
</ItemGroup>
  • Does it mean that in general i need at least 3 PackageReferences for each of Framework/Standard/Core?
  • How to manage the versions when we are stuck with .Net Framework 4.6?
  • I also ran across the issue when i could not run a console .net core app referencing .net standard lib due to nuget versions compatibility problem.

In addition, i'd like to mention that i came across a couple of articles that stated that .csprojs are back and there's no need of .xproj, project.json. Personally, i like this idea to deal with .csprojs like previously.


Context:

I'm not a native English speaker and i'm very new to non-trivial nuget usages and .Net Core/Standard development.

I'm preparing to port a .Net Framework application to .Net Framework+Standard+Core. This is now just a WPF application. But in the future it's intended to stay the same WPF app, but also to have a new .Net Core branch, raising from the existing logic. I don't know exactly what the plans are for the possible new .Net Core branch. Most probably, the plans are to create an ASP.Net Core website. What i know for sure is that the task is to keep logic running as .Net Framework (with WPF UI) and also get it implemented as .Net Core.

I decided to use the combination of Framework+Standard+Core as the solution because of its simplicity. I mean that the conception is easy to understand: base classes (standard) used by 2 brances (FWK, Core). No need of mutitargeting or directives in code (conditional compilation). Implying that the new solution should be created from scratch by copy-pasting and modifying. Still not 100% sure about the reasonability of this decision.

But the question implies that this very approach is to be taken: Framework+Standard+Core.


I hope taht this question is reasonable; i mean that the effective managing of nuget packages in such case is a good start for a successfull cross-platform project. Thanks!


Solution

  • Note: I'm assuming that you're using the new SDK csproj format here, and something like:

    <TargetFrameworks>netstandard1.3;net46;netcoreap1.0</TargetFrameworks>
    

    If the thing that you're referencing exists and targets all the same platforms as your project, it should just work. However, unless you're actually doing something different in Framework vs Standard vs Core, you might as well just target Standard, and in reality: many libs are now rebasing against netstandard2.0 as the minimum. This may or may not be possible for you.

    Versions don't work any differently on 4.6, unless there are problems with specific packages, but: you can get around that by using Condition attributes on the <ItemGroup> or on specific <PackageReference> elements. For example, I have a library that targets net461 and netstandard2.0; and as it happens, everything I'm using is already built into net461, so to avoid having any downstream dependencies, I can do:

      <ItemGroup Condition="'$(TargetFramework)'=='net461'">
        <Reference Include="Microsoft.CSharp" />
      </ItemGroup>
      <ItemGroup Condition="'$(TargetFramework)'!='net461'">
        <PackageReference Include="System.Data.Common" Version="4.3.0" />
        <PackageReference Include="System.Reflection" Version="4.3.0" />
        <PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
        <PackageReference Include="System.Reflection.Emit.ILGeneration" Version="4.3.0" />
        <PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
        <PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
        <PackageReference Include="System.Security.Permissions" Version="4.5.0" />
      </ItemGroup>
    

    This gives me (in nuget):

    enter image description here

    however; I could have left everything as just:

      <ItemGroup>
        <PackageReference Include="System.Data.Common" Version="4.3.0" />
        <PackageReference Include="System.Reflection" Version="4.3.0" />
        <PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
        <PackageReference Include="System.Reflection.Emit.ILGeneration" Version="4.3.0" />
        <PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
        <PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
        <PackageReference Include="System.Security.Permissions" Version="4.5.0" />
      </ItemGroup>
    

    and it would have worked fine - just : the net461 target would list dependencies. I could also have listed completely different dependencies per target framework.