Quais são as implicações de aplicação de uma biblioteca netstandard dependendo de um metapacote?

100

Suponha que eu tenha uma biblioteca de classes que quero direcionar para netstandard1.3, mas também usar BigInteger. Aqui está um exemplo trivial - o único arquivo de origem é 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);            
    }
}

De volta ao mundo de project.json, eu teria como alvo netstandard1.3noframeworks seção e teria uma dependência explícita System.Runtime.Numerics, por exemplo, da versão 4.0.1. O pacote nuget que crio listará apenas essa dependência.

No admirável mundo novo de ferramentas dotnet baseada csproj (estou usando v1.0.1 das ferramentas de linha de comando) há uma referência pacote metapackage implícita para NETStandard.Library 1.6.1quando o direcionamento netstandard1.3. Isso significa que meu arquivo de projeto é muito pequeno, porque não precisa da dependência explícita:

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

... mas o pacote nuget produzido tem uma dependência de NETStandard.Library, o que sugere que, para usar minha pequena biblioteca, você precisa de tudo lá.

Acontece que posso desativar essa funcionalidade usando e DisableImplicitFrameworkReferences, em seguida, adicionar a dependência manualmente novamente:

<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>

Agora meu pacote NuGet diz exatamente do que depende. Intuitivamente, parece um pacote "mais enxuto".

Então, qual é a diferença exata para um consumidor da minha biblioteca? Se alguém tentar usá-lo em um aplicativo UWP, a segunda forma "aparada" de dependências significa que o aplicativo resultante será menor?

Por não documentar DisableImplicitFrameworkReferencesclaramente (pelo que vi; li sobre isso em uma edição ) e por tornar a dependência implícita o padrão ao criar um projeto, a Microsoft está encorajando os usuários a depender apenas do metapacote - mas como posso ser certeza de que não tem desvantagens quando estou produzindo um pacote de biblioteca de classes?

Jon Skeet
fonte
3
Deve-se observar que o corte de dependências está sendo trabalhado . Atualmente, o tamanho de um Hello World!aplicativo independente é reduzido para <10 MB.
ABarney
@ABarney 10MB ainda é demais em comparação com o < 20KB tamanho de um .NET Framework projeto clássico C # Olá-mundo.
Dai

Respostas:

76

No passado, NETStandard.Libraryfornecemos aos desenvolvedores a recomendação de não fazer referência ao meta pacote ( ) dos pacotes NuGet, mas sim fazer referência a pacotes individuais, como System.Runtimee System.Collections. O raciocínio era que pensamos no meta pacote como uma abreviatura para um grupo de pacotes que eram os blocos de construção atômicos reais da plataforma .NET. A suposição era: podemos acabar criando outra plataforma .NET que suporte apenas alguns desses blocos atômicos, mas não todos eles. Conseqüentemente, quanto menos pacotes você referenciar, mais portátil você será. Também havia preocupações sobre como nosso ferramental lida com gráficos de pacotes grandes.

Seguindo em frente, vamos simplificar isso:

  1. .NET Standard é um bloco de construção atômico . Em outras palavras, novas plataformas não têm permissão para criar subconjuntos do .NET Standard - elas precisam implementar tudo isso.

  2. Estamos deixando de usar pacotes para descrever nossas plataformas , incluindo .NET Standard.

Isso significa que você não precisará mais fazer referência a nenhum pacote NuGet para .NET Standard. Você expressou sua dependência com a pasta lib, que é exatamente como funcionou para todas as outras plataformas .NET, em particular .NET Framework.

No entanto, neste momento, nosso ferramental ainda queima na referência a NETStandard.Library. Não há mal nenhum nisso, apenas se tornará redundante no futuro.

Atualizarei as perguntas frequentes sobre o repositório .NET Standard para incluir esta pergunta.

Atualização : Esta questão agora faz parte do FAQ .

Immo Landwerth
fonte
9
Obrigado - isso faz muito sentido. Acho que estava parcialmente confuso porque no mundo project.json eu tive que adicionar dependências mesmo quando os pacotes eram logicamente parte da estrutura que eu pretendia (por exemplo, System.Runtime.Numerics para netstandard1.3) - então eu pensei que NETStandard.Library era realmente puxando-os como dependências.
Jon Skeet
2
Vadio. Eu gostei da ideia de referenciar pacotes, porque parecia que eu poderia referenciar apenas o que eu queria em vez de toda a "pia da cozinha". Será que não estou pensando direito?
Nate Barbettini
3
Você não está necessariamente pensando da maneira errada, mas a questão é por que você se importa. É importante entender que a plataforma não é infinitamente combinável. Quando você executa em um ambiente de estrutura compartilhada (.NET Framework, Mono, .NET Core), todos esses bits estão presentes de qualquer maneira. Se você criar um aplicativo independente (Xamarin, Mono / .NET Core com vinculador), podemos cortar automaticamente com base no seu uso real. De qualquer forma, você não está perdendo nada por não fazer referência a blocos menores.
Immo Landwerth
3
Que tal fazer com que o compilador imponha regras, como impedir que um desenvolvedor faça uma solicitação http em um projeto de lógica de negócios, não permitindo esse pacote - perda de esforço?
David Ferretti
Não necessariamente, mas como você o impõe, ou seja, o que impede um desenvolvedor de adicionar a referência? Eu escreveria um analisador que é injetado no tempo de construção para impor regras de camada e causar erros na máquina de construção.
Immo Landwerth
19

A equipe recomendava descobrir qual era o conjunto de pacotes mais fino. Eles não fazem mais isso e recomendam que as pessoas simplesmente tragam NETStandard.Library (no caso de um projeto no estilo SDK, isso será feito automaticamente para você).

Eu nunca obtive uma resposta totalmente direta sobre o porquê disso, então deixe-me fazer alguns palpites.

O motivo principal provavelmente é que isso permite que eles ocultem as diferenças nas versões das bibliotecas dependentes que, de outra forma, você teria que rastrear ao mudar as estruturas de destino. É também um sistema muito mais amigável com os arquivos de projeto baseados em SDK, porque você francamente não precisa de nenhuma referência para obter uma parte decente da plataforma (como costumava fazer com as referências padrão no Desktop-land, especialmente mscorlib )

Empurrando a meta-definição do que significa ser uma netstandardbiblioteca ou um netcoreappaplicativo para o pacote NuGet apropriado, eles não precisam incluir nenhum conhecimento especial na definição dessas coisas como o Visual Studio (ou dotnet new) os vê.

A análise estática pode ser usada durante a publicação para limitar as DLLs enviadas, algo que eles fazem hoje ao fazer a compilação nativa para UWP (embora com algumas ressalvas). Eles não fazem isso hoje para o .NET Core, mas presumo que seja uma otimização que eles consideraram (além de oferecer suporte a código nativo).

Não há nada que o impeça de ser muito seletivo, se assim escolher. Acredito que você descobrirá que é quase o único a fazer isso, o que também anula o propósito (uma vez que se presume que todos estão trazendo NETStandard.Libraryou Microsoft.NETCore.App).

Brad Wilson
fonte
6
Eu concordo que definitivamente anula o propósito, uma vez que há mais de uma dependência, se alguma delas ocorrer NETStandard.Library. É um tanto auto-realizável, é claro ... se eu depender do NETStandard.LibraryTempo Noda, isso significa que qualquer outra biblioteca construída em cima do Tempo Noda não tem razão para cortar dependências, etc. É tentador ser seletivo agora (o Tempo Noda é em direção a 2.0) e relaxe um pouco mais tarde, uma vez que as convenções tenham sido estabelecidas - mudar de seletivo para baseado em lib seria uma mudança ininterrupta, eu suponho, mas o inverso não é verdade.
Jon Skeet,
8

Você não deve precisar desativar a referência implícita. Todas as plataformas nas quais a biblioteca poderá ser executada já terão os assemblies que a NETStandard.Librarydependência exigiria.

A Biblioteca .NET Standard é uma especificação, um conjunto de assemblies de referência que você compila e que fornece um conjunto de APIs cuja existência é garantida em um conjunto conhecido de plataformas e versões de plataformas, como .NET Core ou .NET Framework . Não é uma implementação desses assemblies, apenas o suficiente da forma da API para permitir que o compilador construa seu código com êxito.

A implementação dessas APIs é fornecida por uma plataforma de destino, como .NET Core, Mono ou .NET Framework. Eles são enviados com a plataforma, porque são uma parte essencial da plataforma. Portanto, não há necessidade de especificar um conjunto de dependências menor - tudo já está lá, você não vai mudar isso.

O NETStandard.Librarypacote fornece esses assemblies de referência. Um ponto de confusão é o número da versão - o pacote é a versão 1.6.1, mas isso não significa ".NET Standard 1.6". É apenas a versão do pacote.

A versão do .NET Standard que você está almejando vem da estrutura de destino especificada em seu projeto.

Se você estiver criando uma biblioteca e quiser que ela seja executada no .NET Standard 1.3, consulte o NETStandard.Librarypacote, atualmente na versão 1.6.1. Mas o mais importante, seu arquivo de projeto seria direcionado netstandard1.3.

O NETStandard.Librarypacote fornecerá um conjunto diferente de assemblies de referência, dependendo de seu moniker de estrutura de destino (estou simplificando para ser breve, mas pense lib\netstandard1.0, lib\netstandard1.1e grupos de dependência ). Portanto, se o seu projeto for direcionado netstandard1.3, você obterá os assemblies de referência 1.3. Se você mirar netstandard1.6, obterá os assemblies de referência 1.6.

Se você estiver criando um aplicativo, não poderá direcionar o .NET Standard. Não faz sentido - você não pode executar em uma especificação. Em vez disso, você visa plataformas de concreto, como net452ou netcoreapp1.1. O NuGet conhece o mapeamento entre essas plataformas e os netstandardidentificadores de estrutura de destino, portanto, sabe quais lib\netstandardX.Xpastas são compatíveis com sua plataforma de destino. Ele também sabe que as dependências de NETStandard.Librarysão satisfeitas pela plataforma de destino, portanto, não puxará nenhum outro assembly.

Da mesma forma, ao criar um aplicativo .NET Core autônomo, os assemblies de implementação do .NET Standard são copiados com seu aplicativo. A referência a NETStandard.Librarynão traz nenhum outro aplicativo novo.

Observe que dotnet publishcriará um aplicativo independente , mas não fará o corte no momento e publicará todos os assemblies. Isso será tratado automaticamente pelo conjunto de ferramentas , portanto, novamente, aparar dependências em sua biblioteca não ajudará aqui.

O único lugar que posso imaginar onde pode ajudar a remover a NETStandard.Libraryreferência é se você está almejando uma plataforma que não oferece suporte ao .NET Standard e você pode encontrar um pacote do .NET Standard onde todas as dependências transitivas podem ser executadas em sua plataforma de destino. Suspeito que não existam muitos pacotes que se encaixem nesse perfil.

cidadão
fonte
Se você fizer isso dotnet publishem um tempo de execução específico, ele trará todas as dependências, incluindo dotnet.exe (ou seu equivalente Linux / OS X). Essa deve ser uma implantação totalmente autônoma nesse ponto. Confira os resultados de um projeto de teste de unidade: gist.github.com/bradwilson/6cc5a8fdfa18230aa6c99b851fb85c01
Brad Wilson
4
Acho que o último parágrafo é precisamente o problema ... e se não fosse um problema, por que precisaríamos de versões diferentes do netstandard1.x? Se toda plataforma tem tudo em netstandard1.6, por que ter netstandard1.0 como conceito? Essa é a parte confusa para mim.
Jon Skeet,
1
E a lista de plataformas que suportam o .NET Standard não inclui NENHUMA das muitas plataformas Unity.
citizenmatt
1
(Sua paciência é muito apreciada, aliás. Eu realmente espero que tudo faça sentido e que seja útil para muitos, muitos desenvolvedores ...)
Jon Skeet
1
Vamos continuar essa discussão no chat .
citizenmatt