Welche Auswirkungen hat eine Netstandard-Bibliothek auf die Anwendung in Abhängigkeit von einem Metapaket?

100

Angenommen, ich habe eine Klassenbibliothek, auf die ich netstandard1.3 abzielen, aber auch verwenden möchte BigInteger. Hier ist ein triviales Beispiel - die einzige Quelldatei ist Adder.cs:

using System;
using System.Numerics;

namespace Calculator
{
    public class Adder
    {
        public static BigInteger Add(int x, int y)
            => new BigInteger(x) + new BigInteger(y);            
    }
}

Zurück in der Welt von project.jsonwürde ich netstandard1.3in diesem frameworksAbschnitt zielen und eine explizite Abhängigkeit von System.Runtime.Numericsz. B. Version 4.0.1 haben. Das von mir erstellte Nuget-Paket listet nur diese Abhängigkeit auf.

In der schönen neuen Welt der csproj-basierten Dotnet-Tools (ich verwende Version 1.0.1 der Befehlszeilentools) gibt es einen impliziten Metapaket-Paketverweis, auf den NETStandard.Library 1.6.1beim Targeting verwiesen wirdnetstandard1.3 . Dies bedeutet, dass meine Projektdatei sehr klein ist, da sie keine explizite Abhängigkeit benötigt:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.3</TargetFramework>
  </PropertyGroup>
</Project>

... aber das produzierte Nuget-Paket hat eine Abhängigkeit von NETStandard.Library, was darauf hindeutet, dass Sie dort alles benötigen, um meine kleine Bibliothek zu nutzen .

Es stellt sich heraus, dass ich diese Funktionalität mithilfe deaktivieren DisableImplicitFrameworkReferencesund die Abhängigkeit dann manuell wieder hinzufügen kann:

<Project Sdk="Microsoft.NET.Sdk">    
  <PropertyGroup>
    <TargetFramework>netstandard1.3</TargetFramework>
    <DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.Runtime.Numerics" Version="4.0.1" />
  </ItemGroup>    
</Project>

Jetzt sagt mein NuGet-Paket genau, worauf es ankommt. Intuitiv fühlt sich das wie ein "schlankeres" Paket an.

Was ist der genaue Unterschied für einen Verbraucher meiner Bibliothek? Wenn jemand versucht, es in einer UWP-Anwendung zu verwenden, bedeutet die zweite "getrimmte" Form von Abhängigkeiten, dass die resultierende Anwendung kleiner ist?

Indem Microsoft nicht DisableImplicitFrameworkReferencesklar dokumentiert (soweit ich gesehen habe; ich habe in einem Problem darüber gelesen ) und die implizite Abhängigkeit beim Erstellen eines Projekts als Standard festgelegt hat, ermutigt Microsoft die Benutzer, sich nur auf das Metapaket zu verlassen - aber wie kann ich das sein? Sicher, dass das keine Nachteile hat, wenn ich ein Klassenbibliothekspaket produziere?

Jon Skeet
quelle
3
Es ist zu beachten, dass an der Trimmung von Abhängigkeiten gearbeitet wird . Derzeit ist die Größe einer Hello World!in sich geschlossenen Anwendung auf <10 MB reduziert.
ABarney
@ABarney 10MB ist immer noch viel zu viel im Vergleich zu der < 20KB- Größe eines klassischen .NET Framework C # Hallo-Welt-Projekts.
Dai

Antworten:

76

In der Vergangenheit haben wir Entwicklern die Empfehlung gegeben, nicht auf das Metapaket ( NETStandard.Library) aus NuGet-Paketen zu verweisen, sondern auf einzelne Pakete wie System.Runtimeund System.Collections. Das Grundprinzip war, dass wir das Metapaket als Abkürzung für eine Reihe von Paketen betrachteten, die die eigentlichen atomaren Bausteine ​​der .NET-Plattform waren. Die Annahme war: Wir könnten am Ende eine andere .NET-Plattform erstellen, die nur einige dieser Atomblöcke unterstützt, aber nicht alle. Je weniger Pakete Sie referenzieren, desto portabler sind Sie. Es gab auch Bedenken, wie unsere Werkzeuge mit großen Paketgraphen umgehen.

In Zukunft werden wir dies vereinfachen:

  1. .NET Standard ist ein atomarer Baustein . Mit anderen Worten, neue Plattformen dürfen .NET Standard nicht unterteilen - sie müssen alles implementieren.

  2. Wir verzichten auf die Verwendung von Paketen zur Beschreibung unserer Plattformen , einschließlich .NET Standard.

Dies bedeutet, dass Sie keine NuGet-Pakete mehr für .NET Standard referenzieren müssen. Sie haben Ihre Abhängigkeit mit dem Ordner lib zum Ausdruck gebracht. Genau so hat es bei allen anderen .NET-Plattformen funktioniert, insbesondere bei .NET Framework.

Im Moment brennen unsere Werkzeuge jedoch noch in der Referenz auf NETStandard.Library. Das schadet auch nicht, es wird in Zukunft nur überflüssig.

Ich werde die FAQ im .NET Standard-Repo aktualisieren , um diese Frage aufzunehmen.

Update : Diese Frage ist jetzt Teil der FAQ .

Immo Landwerth
quelle
9
Danke - das macht sehr viel Sinn. Ich glaube, ich war teilweise verwirrt, weil ich in der project.json-Welt Abhängigkeiten hinzufügen musste, selbst wenn Pakete logisch Teil des Frameworks waren, auf das ich abzielte (z. B. System.Runtime.Numerics für netstandard1.3) - also dachte ich, NETStandard.Library wäre es wirklich als Abhängigkeiten einziehen.
Jon Skeet
2
Schade. Ich mochte die Idee, Pakete zu referenzieren, weil ich das Gefühl hatte, ich könnte nur das referenzieren, was ich wollte, anstatt das ganze "Spülbecken". Vielleicht denke ich nicht richtig darüber nach?
Nate Barbettini
3
Du denkst nicht unbedingt falsch darüber nach, aber die Frage ist, warum es dich interessiert. Es ist wichtig zu verstehen, dass die Plattform nicht unendlich zusammensetzbar ist. Wenn Sie in einer gemeinsam genutzten Framework-Umgebung (.NET Framework, Mono, .NET Core) ausgeführt werden, sind alle diese Bits ohnehin vorhanden. Wenn Sie eine eigenständige App erstellen (Xamarin, Mono / .NET Core mit Linker), können wir diese automatisch basierend auf Ihrer tatsächlichen Nutzung anpassen. In beiden Fällen verlieren Sie nichts, wenn Sie nicht auf kleinere Blöcke verweisen.
Immo Landwerth
3
Wie wäre es, wenn der Compiler Regeln erzwingen würde, z. B. verhindern, dass ein Entwickler eine http-Anfrage in einem Geschäftslogikprojekt stellt, indem er dieses Paket nicht zulässt - Verschwendung von Aufwand?
David Ferretti
Nicht unbedingt, aber wie setzen Sie es durch, dh was hindert einen Entwickler daran, die Referenz hinzuzufügen? Ich würde einen Analysator schreiben, der zur Erstellungszeit injiziert wird, um Ebenenregeln durchzusetzen und Fehler auf der Erstellungsmaschine zu verursachen.
Immo Landwerth
19

Das Team empfahl immer, herauszufinden, was das schlankste Paket war. Sie tun dies nicht mehr und empfehlen, stattdessen nur NETStandard.Library einzubringen (im Fall eines Projekts im SDK-Stil wird dies automatisch für Sie erledigt).

Ich habe nie eine ganz klare Antwort bekommen, warum das so ist. Erlauben Sie mir also, einige fundierte Vermutungen anzustellen.

Der Hauptgrund ist wahrscheinlich, dass sie die Unterschiede in den Versionen der abhängigen Bibliotheken verbergen können, die Sie sonst beim Verfolgen von Ziel-Frameworks selbst verfolgen müssten. Es ist auch ein viel benutzerfreundlicheres System mit den SDK-basierten Projektdateien, da Sie ehrlich gesagt keine Referenzen benötigen, um einen anständigen Teil der Plattform zu erhalten (genau wie Sie es mit den Standardreferenzen in Desktop-Land, insbesondere mscorlib, gewohnt sind ).

Indem sie die Meta-Definition dessen, was es bedeutet, eine netstandardBibliothek oder eine netcoreappAnwendung zu sein, in das entsprechende NuGet-Paket verschieben, müssen sie kein spezielles Wissen in die Definition dieser Dinge einbauen, wie Visual Studio (oder dotnet new) sie sieht.

Während der Veröffentlichung kann eine statische Analyse verwendet werden, um die ausgelieferten DLLs einzuschränken. Dies ist heute bei der nativen Kompilierung für UWP der Fall (allerdings mit einigen Einschränkungen). Das machen sie heute für .NET Core nicht, aber ich nehme an, es ist eine Optimierung, die sie in Betracht gezogen haben (und die native Code-Unterstützung unterstützt).

Nichts hindert Sie daran, sehr selektiv zu sein, wenn Sie dies wünschen. Ich glaube, Sie werden feststellen, dass Sie fast der einzige sind, der dies tut, was auch den Zweck zunichte macht (da davon ausgegangen wird, dass jeder einbringt NETStandard.Libraryoder Microsoft.NETCore.App).

Brad Wilson
quelle
6
Ich bin damit einverstanden, dass es definitiv den Zweck zunichte macht, wenn es mehr als eine Abhängigkeit gibt, wenn eine von ihnen einbringt NETStandard.Library. Es ist natürlich etwas selbsterfüllend ... Wenn ich mich auf NETStandard.LibraryNoda Time verlasse, bedeutet dies, dass jede andere Bibliothek, die auf Noda Time basiert, keinen Grund hat, Abhängigkeiten usw. zu kürzen. Es ist verlockend, vorerst selektiv zu sein (Noda Time ist es) Richtung 2.0), dann etwas später entspannen, sobald Konventionen festgelegt wurden - ein Wechsel von selektiv zu lib-basiert wäre vermutlich eine nicht brechende Änderung, aber das Gegenteil ist nicht der Fall.
Jon Skeet
8

Sie sollten die implizite Referenz nicht deaktivieren müssen. Alle Plattformen, auf denen die Bibliothek ausgeführt werden kann, verfügen bereits über die Assemblys, die für die NETStandard.LibraryAbhängigkeit erforderlich sind.

Die .NET-Standardbibliothek ist eine Spezifikation, eine Reihe von Referenzassemblys, die Sie kompilieren und die eine Reihe von APIs bereitstellen, die garantiert auf einer bekannten Gruppe von Plattformen und Versionen von Plattformen wie .NET Core oder .NET Framework vorhanden sind . Es handelt sich nicht um eine Implementierung dieser Assemblys, sondern nur um eine ausreichende API-Form, damit der Compiler Ihren Code erfolgreich erstellen kann.

Die Implementierung für diese APIs wird von einer Zielplattform wie .NET Core, Mono oder .NET Framework bereitgestellt. Sie werden mit der Plattform geliefert, da sie ein wesentlicher Bestandteil der Plattform sind. Es ist also nicht erforderlich, einen kleineren Abhängigkeitssatz anzugeben - alles ist bereits vorhanden, das werden Sie nicht ändern.

Das NETStandard.LibraryPaket enthält diese Referenzbaugruppen. Ein Punkt der Verwirrung ist die Versionsnummer - das Paket ist Version 1.6.1, dies bedeutet jedoch nicht ".NET Standard 1.6". Es ist nur die Version des Pakets.

Die Version des .NET-Standards, auf die Sie abzielen, stammt aus dem Zielframework, das Sie in Ihrem Projekt angegeben haben.

Wenn Sie eine Bibliothek erstellen und möchten, dass sie unter .NET Standard 1.3 ausgeführt wird, verweisen Sie auf das NETStandard.LibraryPaket, das derzeit in Version 1.6.1 verfügbar ist. Noch wichtiger ist jedoch, dass Ihre Projektdatei darauf abzielt netstandard1.3.

Das NETStandard.LibraryPaket wird Ihnen einen anderen Satz von Referenzeinheiten auf dem Zielrahmen Moniker abhängig (I Kürze halber bin Vereinfachung, aber denken lib\netstandard1.0, lib\netstandard1.1und Abhängigkeitsgruppen ). Wenn Ihr Projekt Ziele hat netstandard1.3, erhalten Sie die 1.3 Referenzbaugruppen. Wenn Sie ein Ziel netstandard1.6festlegen, erhalten Sie die 1.6-Referenzbaugruppen.

Wenn Sie eine Anwendung erstellen, können Sie nicht auf den .NET-Standard abzielen. Es macht keinen Sinn - Sie können nicht auf einer Spezifikation laufen. Stattdessen zielen Sie auf konkrete Plattformen wie net452oder ab netcoreapp1.1. NuGet kennt die Zuordnung zwischen diesen Plattformen und den netstandardZiel-Framework-Monikern und weiß daher, welche lib\netstandardX.XOrdner mit Ihrer Zielplattform kompatibel sind. Es ist auch bekannt, dass die Abhängigkeiten von NETStandard.Libraryvon der Zielplattform erfüllt werden, sodass keine anderen Assemblys abgerufen werden.

Ebenso werden beim Erstellen einer eigenständigen .NET Core-App die .NET Standard-Implementierungsassemblys mit Ihrer App kopiert. Der Verweis auf NETStandard.Librarybringt keine anderen neuen Apps.

Beachten Sie, dass dadurch dotnet publisheine eigenständige Anwendung erstellt wird , derzeit jedoch kein Zuschneiden durchgeführt wird und alle Assemblys veröffentlicht werden. Dies wird automatisch durch Tools erledigt . Auch hier hilft das Trimmen von Abhängigkeiten in Ihrer Bibliothek nicht weiter.

Ich kann mir nur vorstellen, wo es hilfreich sein könnte , die NETStandard.LibraryReferenz zu entfernen, wenn Sie auf eine Plattform abzielen, die den .NET-Standard nicht unterstützt, und Sie ein Paket aus dem .NET-Standard finden, in dem alle transitiven Abhängigkeiten ausgeführt werden können auf Ihrer Zielplattform. Ich vermute, es gibt nicht viele Pakete, die zu dieser Rechnung passen würden.

bürgermatt
quelle
Wenn Sie dotnet publisheine bestimmte Laufzeit festlegen, werden alle Abhängigkeiten einschließlich dotnet.exe (oder dessen Linux / OS X-Äquivalent) berücksichtigt. Dies sollte zu diesem Zeitpunkt eine vollständig eigenständige Bereitstellung sein. Überprüfen Sie die Ergebnisse für ein Unit-Test-Projekt: gist.github.com/bradwilson/6cc5a8fdfa18230aa6c99b851fb85c01
Brad Wilson
4
Ich denke, der letzte Absatz ist genau das Problem ... und wenn es kein Problem wäre, warum würden wir überhaupt verschiedene Versionen von netstandard1.x benötigen? Wenn jede Plattform alles in netstandard1.6 hat, warum auch hast netstandard1.0 als ein Konzept? Das ist der verwirrende Teil für mich.
Jon Skeet
1
Die Liste der Plattformen, die den .NET-Standard unterstützen, enthält KEINE der vielen Unity-Plattformen.
Citizenmatt
1
(Ihre Geduld wird übrigens sehr geschätzt. Ich hoffe wirklich, dass dies alles Sinn macht und dass es vielen, vielen Entwicklern helfen wird ...)
Jon Skeet
1
Lassen Sie uns diese Diskussion im Chat fortsetzen .
Citizenmatt