O MSBuild não copia referências (arquivos DLL) se estiver usando dependencies de projeto na solução

Eu tenho quatro projetos na minha solução Visual Studio (todos visando .NET 3.5) – para o meu problema, apenas estes dois são importantes:

  1. MyBaseProject <- esta biblioteca de classes faz referência a um arquivo DLL de terceiros (elmah.dll)
  2. MyWebProject1 <- este projeto de aplicação web tem uma referência ao MyBaseProject

Eu adicionei a referência elmah.dll para MyBaseProject no Visual Studio 2008, clicando em “Adicionar referência …” → “Procurar” guia → selecionando o “elmah.dll”.

As propriedades da referência Elmah são as seguintes:

  • Aliases – global
  • Copiar local – verdadeiro
  • Cultura –
  • Descrição – Módulos e manipuladores de registro de erros (ELMAH) para o ASP.NET
  • Tipo de arquivo – assembly
  • Caminho – D: \ webs \ otherfolder \ _myPath \ __ tools \ elmah \ Elmah.dll
  • Resolvido – Verdadeiro
  • Versão de tempo de execução – v2.0.50727
  • Versão especificada – falsa
  • Nome forte – falso
  • Versão – 1.0.11211.0

Em MyWebProject1 eu adicionei a referência ao Project MyBaseProject por: “Add reference …” → “Projects” aba → selecionando o “MyBaseProject”. As propriedades desta referência são as mesmas, exceto os seguintes membros:

  • Descrição –
  • Caminho – D: \ webs \ CMS \ MyBaseProject \ bin \ Debug \ MyBaseProject.dll
  • Versão – 1.0.0.0

Se eu executar a compilation no Visual Studio, o arquivo elmah.dll é copiado para o diretório bin do MyWebProject1 , juntamente com o MyBaseProject.dll!

No entanto, se eu limpar e executar o MSBuild para a solução (via D: \ webs \ CMS> C: \ WINDOWS \ Microsoft.NET \ Framework \ v3.5 \ MSBuild.exe / t: ReBuild / p: configuração = debugging MyProject.sln ) o elmah.dll está faltando no diretório bin do MyWebProject1 – embora a compilation não contenha nenhum aviso ou erro!

Eu já tinha certeza que o .csproj do MyBaseProject contém o elemento private com o valor “true” (que deveria ser um apelido para ” copy local ” no Visual Studio):

 False ..\mypath\__tools\elmah\Elmah.dll **true**  

(A marca privada não apareceu no xml do .csproj por padrão, embora o Visual Studio tenha dito “cópia local” como verdadeira. Troquei “copiar local” para falso – salvo – e defini-lo de volta para verdadeiro – salvar!)

O que há de errado com o MSBuild? Como faço para obter a referência (elmah.dll) copiada para bin do MyWebProject1?

Eu não quero adicionar uma ação de cópia postbuild para o comando postbuild de cada projeto! (Imagine que eu teria muitos projetos dependendo do MyBaseProject!)

Não sei por que é diferente quando estou construindo entre o Visual Studio e o MsBuild, mas aqui está o que eu encontrei quando encontrei esse problema no MsBuild e no Visual Studio.

Explicação

Para um cenário de amostra, digamos que temos o projeto X, o assembly A e o assembly B. O Assembly A referencia o assembly B, então o projeto X inclui uma referência tanto ao A como ao B. O projeto X inclui o código que faz referência ao assembly A (por exemplo, A. SomeFunction ()). Agora, você cria um novo projeto Y que faz referência ao projeto X.

Portanto, a cadeia de dependência é assim: Y => X => A => B

O Visual Studio / MSBuild tenta ser inteligente e trazer apenas referências para o projeto Y que ele detecta como sendo requerido pelo projeto X; ele faz isso para evitar a poluição de referência no projeto Y. O problema é que, como o projeto X não contém qualquer código que use explicitamente o conjunto B (por exemplo, B.SomeFunction ()), o VS / MSBuild não detecta que B é necessário por X, e assim não copia para o diretório bin do projeto Y; apenas copia os conjuntos X e A.

Solução

Você tem duas opções para resolver esse problema, e ambos resultarão na cópia do conjunto B para o diretório bin do projeto Y:

  1. Adicione uma referência ao assembly B no projeto Y.
  2. Adicione código fictício a um arquivo no projeto X que usa o assembly B.

Pessoalmente eu prefiro a opção 2 por algumas razões.

  1. Se você adicionar outro projeto no futuro que faça referência ao projeto X, não precisará se lembrar de include também uma referência à assembly B (como você faria com a opção 1).
  2. Você pode ter comentários explícitos dizendo por que o código fictício precisa estar lá e não removê-lo. Portanto, se alguém excluir o código por acidente (digamos, com uma ferramenta de refatoração que procura código não utilizado), você poderá ver facilmente do controle de origem que o código é necessário e restaurá-lo. Se você usar a opção 1 e alguém usar uma ferramenta de refatoração para limpar referências não usadas, você não tem comentários; você verá apenas que uma referência foi removida do arquivo .csproj.

Aqui está uma amostra do “código fictício” que eu normalmente adiciono quando me deparo com essa situação.

  // DO NOT DELETE THIS CODE UNLESS WE NO LONGER REQUIRE ASSEMBLY A!!! private void DummyFunctionToMakeSureReferencesGetCopiedProperly_DO_NOT_DELETE_THIS_CODE() { // Assembly A is used by this file, and that assembly depends on assembly B, // but this project does not have any code that explicitly references assembly B. Therefore, when another project references // this project, this project's assembly and the assembly A get copied to the project's bin directory, but not // assembly B. So in order to get the required assembly B copied over, we add some dummy code here (that never // gets called) that references assembly B; this will flag VS/MSBuild to copy the required assembly B over as well. var dummyType = typeof(B.SomeClass); Console.WriteLine(dummyType.FullName); } 

Eu apenas lidei com isso assim. Vá para as propriedades da sua referência e faça o seguinte:

 Set "Copy local = false" Save Set "Copy local = true" Save 

e é isso.

O Visual Studio 2010 não coloca inicialmente: True na tag de referência e a configuração “copy local” como false faz com que ele crie a tag. Depois disso, ele será definido como verdadeiro e falso de acordo.

Se você não estiver usando o assembly diretamente no código, o Visual Studio, embora tente ser útil, detecta que não é usado e não o inclui na saída. Não tenho certeza por que você está vendo um comportamento diferente entre o Visual Studio e o MSBuild. Você pode tentar definir a saída de construção como diagnóstico para ambos e comparar os resultados para ver onde ela diverge.

Quanto a sua referência elmah.dll se você não está referenciando-o diretamente no código você pode adicioná-lo como um item para o seu projeto e definir a ação de compilation para o Content e a cópia para o diretório de saída para Always .

Dê uma olhada:

Este tópico do fórum MSBuild eu comecei

Você encontrará minha solução temporária / solução lá!

(MyBaseProject precisa de algum código que está referenciando algumas classs (qualquer que seja) do elmah.dll para elmah.dll sendo copiado para bin do MyWebProject1!)

Eu tive o mesmo problema.

Verifique se a versão do framework do seu projeto é a mesma da versão do framework da dll que você colocou na referência.

No meu caso, meu cliente foi compilado usando o “Framework 4 Client” e a DLL estava no “Framework 4”.

O problema que eu estava enfrentando era que eu tenho um projeto que depende de um projeto de biblioteca. Para construir eu estava seguindo estes passos:

 msbuild.exe myproject.vbproj /T:Rebuild msbuild.exe myproject.vbproj /T:Package 

Isso, é claro, significava que eu estava sentindo falta dos arquivos dll da minha biblioteca no bin e, o mais importante, no arquivo zip do pacote. Eu achei isso funciona perfeitamente:

 msbuild.exe myproject.vbproj /T:Rebuild;Package 

Eu não tenho ideia de por que esse trabalho ou por que não aconteceu em primeiro lugar. Mas espero que ajude.

Como Alex Burtsev mencionou em um comentário qualquer coisa que é usada apenas em um dictionary de resources XAML, ou no meu caso, qualquer coisa que é usada apenas em XAML e não em código por trás, não é considerada ’em uso’ pelo MSBuild.

Portanto, simplesmente criar uma referência fictícia a uma class / componente na assembly em algum código por trás foi suficiente para convencer o MSBuild de que o assembly estava realmente em uso.

A alteração da estrutura de destino do .NET Framework 4 Client Profile para o .NET Framework 4 corrigiu esse problema para mim.

Portanto, no seu exemplo: defina a estrutura de destino no MyWebProject1 para o .NET Framework 4

Acabei de ter exatamente o mesmo problema e acabou sendo causado pelo fato de dois projetos na mesma solução referenciarem uma versão diferente da biblioteca de terceiros.

Depois de corrigir todas as referências, tudo funcionou perfeitamente.

Eu tive o mesmo problema e a dll era uma referência carregada dinamicamente. Para resolver o problema eu adicionei um “usando” com o namespace da dll. Agora a dll é copiada na pasta de saída.

Isso requer adicionar um arquivo .targets ao seu projeto e defini-lo para ser incluído na seção includes do projeto.

Veja minha resposta aqui para o procedimento.

Fazer referência a conjuntos que não são usados ​​durante a compilation não é a prática correta. Você deve aumentar seu arquivo de construção para copiar os arquivos adicionais. Ou usando um evento post build ou atualizando o grupo de propriedades.

Alguns exemplos podem ser encontrados em outro post

  • MSBuild para copiar arquivos gerados dinamicamente como parte da dependência do projeto
  • VS2010 Como include arquivos no projeto, para copiá-los para construir o diretório de saída automaticamente durante a construção ou publicação

Usando o esquema do cão mortal,

Y => X => A => B ,

meu problema era quando eu criava Y, os assemblies (A e B, todos os 15 deles) do X não estavam aparecendo na pasta bin do Y.

Eu resolvi isso removendo a referência X de Y, save, build, e então adicionei novamente a referência X (uma referência de projeto), e salve, construa, e A e B começaram a aparecer na pasta bin de Y.

Outro cenário em que isso é exibido é se você estiver usando o tipo de projeto “Site da Web” mais antigo no Visual Studio. Para esse tipo de projeto, não é possível referenciar .dlls que estão fora de sua própria estrutura de diretório (pasta atual e abaixo). Então, na resposta acima, digamos que sua estrutura de diretórios seja assim:

insira a descrição da imagem aqui

Onde ProjectX e ProjectY são diretórios pai / filho e referências ProjectX A.dll que, por sua vez, referencia B.dll, e B.dll está fora da estrutura de diretórios, como em um pacote Nuget na raiz (Pacotes), em seguida, A. dll será incluído, mas B.dll não será.

Acabei de me deparar com um problema muito semelhante. Ao compilar usando o Visual Studio 2010, o arquivo DLL foi incluído na pasta bin . Mas ao compilar usando o MSBuild o arquivo DLL de terceiros não foi incluído.

Muito frustrante. A maneira como resolvi isso foi include a referência do NuGet ao pacote no meu projeto da Web, embora eu não esteja usando diretamente nele.

Eu tive um problema semelhante hoje, e isso certamente não é a resposta para sua pergunta. Mas gostaria de informar a todos e, possivelmente, fornecer uma centelha de insight.

Eu tenho um aplicativo asp.net. O processo de compilation está definido para limpar e, em seguida, construir.

Eu tenho dois scripts de CI do Jenkins . Um para produção e outro para encenação. Eu implantei meu aplicativo para preparação e tudo funcionou bem. Implantado na produção e estava faltando um arquivo DLL que foi referenciado. Este arquivo DLL estava apenas na raiz do projeto. Não está em nenhum repository do NuGet. A DLL foi definida para do not copy .

O script de IC e o aplicativo foram os mesmos entre as duas implementações. Ainda após a limpeza e implementação no ambiente de preparação, o arquivo DLL foi substituído no local de implantação do aplicativo ASP.NET ( bin/ ). Este não foi o caso para o ambiente de produção.

Acontece que em uma ramificação de teste eu adicionei uma etapa ao processo de compilation para copiar esse arquivo DLL para o diretório bin . Agora a parte que demorou um pouco para descobrir. O processo de IC não estava se limpando. A DLL foi deixada no diretório de trabalho e foi acidentalmente empacotada com o arquivo .zip do ASP.NET. O ramo de produção nunca teve o arquivo DLL copiado da mesma maneira e nunca foi implantar isso acidentalmente.

TLDR; Verifique e certifique-se de saber o que seu servidor de compilation está fazendo.

Incluir todos os arquivos DLL referenciados de suas preferências de projeto no projeto do site nem sempre é uma boa idéia, especialmente quando você está usando injeção de dependência : seu projeto da web só deseja adicionar uma referência ao arquivo / projeto da interface DLL, não qualquer implementação concreta DLL Arquivo.

Porque, se você adicionar uma referência diretamente a um arquivo / projeto de DLL de implementação, não poderá impedir que seu desenvolvedor chame um “novo” em classs concretas do arquivo / projeto da DLL de implementação, em vez da interface. É também que você declarou um “código” em seu site para usar a implementação.