Skip to content

Instantly share code, notes, and snippets.

@nguerrera
Last active December 19, 2018 00:29
Show Gist options
  • Save nguerrera/ddaee3a0276e99cad313f3e8de7d42fd to your computer and use it in GitHub Desktop.
Save nguerrera/ddaee3a0276e99cad313f3e8de7d42fd to your computer and use it in GitHub Desktop.
FrameworkReferenceFAQ

See also https://github.com/dotnet/designs-microsoft/pull/38

This was put together very quickly in preparation for talking points at today's meeting. I intend to rework this and combine it with above and also gather other documentation for the larger picture of "framework composition" into one place, but I have not had time to complete that yet.

FrameworkReference FAQ

What is a FrameworkReference?

A new MSBuild item that represents a reference to a well-known group of framework assemblies that are versioned with the project's TargetFramework.

For .NET Core, there are two use cases:

  • Reference to entire shared framework
 <FrameworkReference Include="Microsoft.NETCore.App" />
 <FrameworkReference Include="Microsoft.AspNetCore.App" />
  • Reference to named subset or "profile" of a shared framework
<FrameworkReference Include="Microsoft.WindowsDesktop.App|WindowsForms" />
<FrameworkReference Include="Microsoft.WindowsDesktop.App|WPF" />

Originally, we wanted to keep things 1:1 with .NET Core shared frameworks, but there are technical reasons why WindowsForms and WPF are going to stay in the same framework, but must not always be referenced together as that degrades the design-time experience.

We haven't discussed the syntax for this case yet, and I just invented <shared framework>|<profile name>. Open to alternative schemes. Encoding it into a single string is appealing so that there is still just a single name to record for anything in the system that does not need to understand how these relate to packages or assemblies. This will be particularly useful if we decide to reuse .nuspec frameworkAssemblies to record package -> framework references.

There will be a file list in the targeting pack that identifies which assemblies are in the targeting pack and to which profile(s) they belong.

How are FrameworkReferences added to a project?

Any of the following ways:

  1. Implicit <FrameworkReference Include="..."> in MSBuild SDK.
  2. Transitively through project references
  3. Transtively through package references
  4. Explicit <FrameworkReference Include="..." /> in user project file

The implicit framework references brokwn down by MSBuild SDK for netcore3.0 are as follows:

  1. Microsoft.NET.Sdk

    • "Microsoft.NETCore.App"
  2. Microsoft.NET.Sdk.Web

    • "Microsoft.AspNetCore.App"
    • "Microsoft.NETCore.App" (via chained Microsoft.NET.Sdk)
  3. Microsoft.NET.Sdk.WindowsDesktop

    • "Microsoft.WindowsDesktop.App" if both $(UseWPF) and $(UseWindowsForms) are true
    • "Microsoft.WindowsDesktop.App|WPF if only $(UseWPF) is true
    • "Microsoft.WindowsDesktop.App|WindowsForms" if only $(UseWindowsForms) is true
    • "Microsoft.NETCore.App" (via chained Microsoft.NET.Sdk)

Just as with the older framework package reference, $(DisableImplicitFrameworkReferences) should be honoured and prevent the SDKs from adding these implicit FrameworkReferences.

We probably also need an option to disable transitive FrameworkReferences. I propose (DisableTransitiveFrameworkReferences), which would mirror existing $(DisableTransitiveProjectReferences). (We already have $(DisableLockFileFrameworks) that would likely do this naturally if we represent FrameworkReferences in assets file as frameworkAssemblies, but the name isn't very good as it's tied to an implementation detail.)

Transitivity is to be achieved via recording when a ProjectReference or PackageReference needs a FrameworkReference in the assets file during NuGet restore: NuGet/Home#7342

Why not just use Reference?

Other parts of the system are hard-wired to 1 Reference:1 Assembly. Like PackageReferences, FrameworkReferences will resolve each FrameworkReference down to multiple, constituent References before ResolveAssemblyReferences runs.

There are also way too many assemblies in Microsoft.NETCore.App and Microsoft.AspNETCore.App to productively reference individually.

Why not just use PackageReference?

In short, because we tried it already .NET Core 2.x, and it did not work very well. Framework asset resolution implemented via standard nuget package resolution is a leaky abstraction:

  • It's terribly confusing when the package is upgraded and either:

    1. The app can no longer run on unpatched machines
    2. Framework assemblies are suddenly included in your app.
  • It's even more confusing to encounter a downgrade error.

Here is a sampling of issues that Nate McMaster helpfully curated:

Furthermore, the correct resolution of assets from a FrameworkReference is different from a PackageReference. To see this, imagine if .NET Framework References were transitive and implemented as PackageReferences.

Now imagine this graph:

  • A (net45):

    • <PackageReference Include="mscorlib" Version="4.5.0">
    • <PackageReference Include="System" Version="4.5.0">
    • <ProjectReference Include="B.csproj" />
  • B (net40):

    • <PackageReference Include="mscorlib" Version="4.0.0">
    • <PackageReference Include="System" Version="4.0.0">
    • <PackageReference Include="System.xml" Version="4.0.0">
  • Applying standard nuget transitivity, A gets references:

    • B
    • mscorlib, 4.5.0
    • System, 4.5.0
    • System.Xml, 4.0.0 <-- Oops!

In this example, for A to use B, System.Xml is needed, but it makes no sense to use .NET 4.0 System.Xml in .NET 4.5. So A must unify to .NET 4.5 System.Xml. In the case of System and mscorlib, this happened because A also references them directly.

The reality is that framework assets are special, and trying to express the rules as nuget rules will either be incorrect or add significant new complexity to nuget.

If NuGet writes FrameworkReferences to assets file, shouldn't it also be responsible for determining FrameworkReference assets and listing them in assets file?

Think of writing FrameworkReferences to assets file as morally equivalent to writing References as frameworkAssemblies. The process of consuming them will be nearly the same: instead of raising Reference items from the assets file, we raise FrameworkReference. NuGet does not need to know anything about them other than their names.

Abstractly, FrameworkReferences are not directly coupled to packages. In common cases, they will be resolved offline from Program Files\dotnet... without any packages being used. The assets are only downloaded in packages to cover the scenario of building for a downlevel TFM where you don't have the targeting packs installed globally. We proposed an orthogonal DownloadOnlyPackage feature for this case (and also for "runtime packs" not covered here).

This allows the SDK to be fully in control of how FrameworkReferences are resolved to files on disk without baking more concepts into NuGet.

However, we are still looking at complications that arise for the IDE due to this. I would prefer that we indicate to the IDE how files were resolved to framework references via a different process than the assets file. Ditto for errors. That said, I am open to tieng more concepts to the assets file, if we can do it without the problematic issues discussed above leaking back in. So, it's not simply a case of reverting to PackageReferences, there would still be a new concept in NuGet and it would be a more complicated new concept than DownloadOnlyPackage for NuGet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment