web-dev-qa-db-fra.com

MSBuild ne copie pas les références (fichiers DLL) si des dépendances de projet sont utilisées dans la solution.

J'ai quatre projets dans ma solution Visual Studio (tout le monde cible .NET 3.5). Pour mon problème, seuls ces deux sont importants:

  1. MyBaseProject _ <- cette bibliothèque de classes fait référence à un fichier tiers DLL (elmah.dll)
  2. MyWebProject1 <- ce projet d'application Web fait référence à MyBaseProject.

J'ai ajouté la référence elmah.dll à MyBaseProject dans Visual studio 2008 en cliquant sur "Ajouter une référence ..." → onglet "Parcourir" → en sélectionnant "elmah.dll".

Les propriétés de la référence Elmah sont les suivantes:

  • Alias ​​- global
  • Copier local - vrai
  • Culture -
  • Description - Modules de journalisation d’erreur et gestionnaires (ELMAH) pour ASP.NET
  • Type de fichier - Assemblage
  • Chemin d'accès - D:\webs\otherfolder\_myPath\__ tools\elmah\Elmah.dll
  • Résolu - Vrai
  • Version d'exécution - v2.0.50727
  • Version spécifiée - false
  • Nom fort - faux
  • Version - 1.0.11211.0

Dans MyWebProject1, j'ai ajouté la référence au projet MyBaseProject de: "Ajouter une référence ..." → onglet "Projets" → en sélectionnant le "MyBaseProject". Les propriétés de cette référence sont les mêmes, à l'exception des membres suivants:

  • La description -
  • Chemin d'accès - D:\webs\CMS\MyBaseProject\bin\Debug\MyBaseProject.dll
  • Version - 1.0.0.0

Si j'exécute la construction dans Visual Studio, le fichier elmah.dll est copié dans mon répertoire bin de MyWebProject1, avec MyBaseProject.dll!

Cependant, si je nettoie et exécute MSBuild pour la solution (via D:\webs\CMS> C:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe/t: ReBuild/p: Configuration = Déboguer MyProject.sln) Le elmah.dll est manquant dans le répertoire bin de MyWebProject1 - bien que la construction elle-même ne contienne aucun avertissement ou erreur!

Je m'étais déjà assuré que le .csproj de MyBaseProject contenait l'élément privé avec la valeur "true" (il devrait s'agir d'un alias pour "copie locale" dans Visual Studio):

<Reference Include="Elmah, Version=1.0.11211.0, Culture=neutral, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <HintPath>..\mypath\__tools\elmah\Elmah.dll</HintPath>
    **<Private>true</Private>**
</Reference>

(La balise privée n'apparaissait pas dans le fichier XML de .csproj par défaut, bien que Visual Studio ait déclaré "copie locale" true. J'ai basculé "copie locale" sur false - enregistré - et le réinitialiser à nouveau - enregistrer!)

Quel est le problème avec MSBuild? Comment puis-je copier la référence (elmah.dll) dans le répertoire bin de MyWebProject1?

Je ne veux PAS ajouter une action de copie post-construction à la commande post-construction de chaque projet! (Imaginez que de nombreux projets dépendent de MyBaseProject!)

259
toebens

Je ne sais pas pourquoi c'est différent lors de la génération entre Visual Studio et MsBuild, mais voici ce que j'ai trouvé lorsque j'ai rencontré ce problème dans MsBuild et Visual Studio.

Explication

Pour un exemple de scénario, supposons que nous ayons le projet X, l’assemblage A et l’assemblage B. L’assemblage A fait référence à l’assemblage B, le projet X inclut donc une référence à la fois à A et B. En outre, le projet X comprend un code faisant référence à l’assemblage A (par exemple, SomeFunction ()). Vous créez maintenant un nouveau projet Y qui référence le projet X.

Donc, la chaîne de dépendance ressemble à ceci: Y => X => A => B

Visual Studio/MSBuild essaie d’être intelligent et n’apporte que les références dans le projet Y qu’il détecte comme étant requises par le projet X; Pour éviter la pollution de référence dans le projet Y, le problème est que, comme le projet X ne contient aucun code utilisant explicitement Assembly B (par exemple, B.SomeFunction ()), VS/MSBuild ne détecte pas que B est requis. par X et ne le copie donc pas dans le répertoire bin du projet Y; il ne copie que les assemblages X et A.

Solution

Vous avez le choix entre deux solutions pour résoudre ce problème. Dans les deux cas, l'assemblage B sera copié dans le répertoire bin du projet Y:

  1. Ajoutez une référence à Assembly B dans le projet Y.
  2. Ajoutez du code factice à un fichier du projet X qui utilise Assembly B.

Personnellement, je préfère l’option 2 pour quelques raisons.

  1. Si vous ajoutez ultérieurement un autre projet faisant référence au projet X, vous n'aurez plus à vous rappeler d'inclure également une référence à Assembly B (comme dans le cas de l'option 1).
  2. Vous pouvez avoir des commentaires explicites expliquant pourquoi le code factice doit être présent et ne pas le supprimer. Ainsi, si quelqu'un supprime le code par accident (par exemple, avec un outil de refactorisation qui recherche le code inutilisé), vous pouvez facilement voir dans le contrôle de source que le code est requis et le restaurer. Si vous utilisez l'option 1 et que quelqu'un utilise un outil de refactorisation pour nettoyer les références inutilisées, vous n'avez aucun commentaire. vous verrez seulement qu'une référence a été supprimée du fichier .csproj.

Voici un exemple du "code factice" que j’ajoute généralement lorsque je rencontre cette situation.

    // 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);
    }
141
deadlydog

Je viens de traiter comme ça. Accédez aux propriétés de votre référence et procédez comme suit:

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

et c'est tout.

Visual Studio 2010 n'a pas initialement mis: <private>True</private> dans la balise de référence et le réglage de "copie locale" sur false entraîne la création de la balise. Ensuite, il sera défini sur true et false en conséquence.

165
andrew

Si vous n'utilisez pas Assembly directement dans le code, alors Visual Studio tente de vous aider en détectant qu'il n'est pas utilisé et ne l'inclut pas dans la sortie. Je ne sais pas pourquoi vous constatez un comportement différent entre Visual Studio et MSBuild. Vous pouvez essayer de définir la sortie de génération sur Diagnostic et de comparer les résultats pour voir où elle diverge.

En ce qui concerne votre référence elmah.dll, si vous ne le référencez pas directement dans le code, vous pouvez l'ajouter en tant qu'élément à votre projet et définir l'action de génération sur Content et le paramètre Copy to Output Directory sur Always.

38
John Hunter

Jeter un coup d'œil à:

J'ai commencé ce fil de discussion MSBuild

Vous trouverez ma solution temporaire/solution de contournement ici!

(MyBaseProject a besoin de code référençant des classes (peu importe) de elmah.dll pour elmah.dll copié dans le fichier bin de MyWebProject1!)

14
toebens

J'ai eu le même problème.

Vérifiez si la version d'infrastructure de votre projet est identique à la version d'infrastructure de la dll que vous avez mise en référence.

Dans mon cas, mon client a été compilé avec "Framework 4 Client" et la DLL était dans "Framework 4".

8
Andre Avelar

La question à laquelle je faisais face était que j'ai un projet qui dépend d'un projet de bibliothèque. Afin de construire je suivais ces étapes:

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

Cela signifiait bien sûr qu'il me manquait les fichiers dll de ma bibliothèque dans bin et surtout dans le fichier Zip. J'ai trouvé cela fonctionne parfaitement:

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

Je ne sais pas pourquoi ce travail ou pourquoi il ne l'a pas fait en premier lieu. Mais espérons que cela aide.

6
vangorra

Comme Alex Burtsev a mentionné dans un commentaire, tout élément utilisé uniquement dans un dictionnaire de ressources XAML, ou dans mon cas, tout élément utilisé uniquement dans XAML et non dans le code suivant, n'est pas considéré comme "utilisé" par MSBuild. 

Il était donc suffisant de convaincre MSBuild que l’utilisation de Assembly était réellement utilisée.

4
Scott

Je viens d'avoir exactement le même problème et cela s'est avéré dû au fait que 2 projets de la même solution faisaient référence à une version différente de la bibliothèque tierce. 

Une fois que j'ai corrigé toutes les références, tout fonctionnait parfaitement.

3
Steven Engels

En utilisant le schéma de deadlydog, 

Y => X => A => B

mon problème était quand j'ai construit Y, les assemblys (A et B, les 15 d'entre eux) de X n'apparaissaient pas dans le dossier bin de Y.

Je l'ai résolu en supprimant la référence X de Y, en enregistrant, en construisant, puis en rajoutant une référence X (une référence de projet), puis en enregistrant, en construisant, et A et B ont commencé à apparaître dans le dossier bin de Y.

3
J Z

Changer le framework cible de .NET Framework 4 Client Profile à .NET Framework 4 a résolu ce problème pour moi.

Ainsi, dans votre exemple: définissez le cadre cible de MyWebProject1 sur .NET Framework 4

3
Fuyu Persimmon

Cela nécessite l'ajout d'un fichier .targets à votre projet et sa définition pour l'inclure dans la section includes du projet.

Voir ma réponse ici pour la procédure.

2
Alex Essilfie

Référencer des assemblys qui ne sont pas utilisés lors de la construction n'est pas la bonne pratique. Vous devriez augmenter votre fichier de construction pour qu'il copie les fichiers supplémentaires. Soit en utilisant un événement post-build ou en mettant à jour le groupe de propriétés. 

Quelques exemples peuvent être trouvés dans d'autres post

2
verbedr

J'ai eu le même problème et la dll était une référence chargée dynamiquement . Pour résoudre le problème, j'ai ajouté un "using" avec l'espace de noms de la dll . La dll est maintenant copiée dans le dossier de sortie.

2
Spamme

Si vous utilisez l'ancien type de projet "Site Web" dans Visual Studio, cela est un autre scénario. Pour ce type de projet, il est impossible de faire référence à des fichiers .dll situés en dehors de sa propre structure de répertoires (dossier actuel et vers le bas). Donc, dans la réponse ci-dessus, supposons que votre structure de répertoires ressemble à ceci:

 enter image description here

Où ProjectX et ProjectY sont des répertoires parent/enfant, et ProjectX fait référence à A.dll, qui à son tour fait référence à B.dll et B.dll est en dehors de la structure de répertoires, comme dans un package Nuget à la racine (Packages), puis A. dll sera inclus, mais pas B.dll. 

2
Focker

Assurez-vous que les deux projets sont dans la même version .net et cochez la propriété de copie locale, mais ceci devrait être true comme valeur par défaut

0
john.kernel

J'ai eu un problème similaire aujourd'hui, et ce n'est certainement pas la réponse à votre question. Mais je voudrais informer tout le monde et éventuellement fournir une étincelle de perspicacité.

J'ai une application ASP.NET. Le processus de génération est défini pour nettoyer puis construire.

J'ai deux JenkinsCI scripts. Un pour la production et un pour la mise en scène. J'ai déployé mon application pour la mise en scène et tout a bien fonctionné. Déployé en production et manquant d'un fichier DLL référencé. Ce fichier DLL était juste à la racine du projet. Pas dans aucun référentiel NuGet. La DLL était définie sur do not copy.

Le script CI et l'application étaient les mêmes entre les deux déploiements. Toujours après le nettoyage et le déploiement dans l'environnement de transfert, le fichier DLL a été remplacé à l'emplacement de déploiement de l'application ASP.NET (bin/). Ce n'était pas le cas pour l'environnement de production.

Il s'avère que dans une branche de test, j'avais ajouté une étape dans le processus de construction pour copier ce fichier DLL dans le répertoire bin. Maintenant, la partie qui a pris un peu de temps à comprendre. Le processus de CI ne se nettoyait pas. La DLL était laissée dans le répertoire de travail et était empaquetée accidentellement avec le fichier ASP.NET .Zip. Le fichier DLL n'a jamais été copié de la même manière dans la branche de production et ne l'a jamais déployé par inadvertance.

TLDR; Vérifiez et assurez-vous que vous savez ce que fait votre serveur de build.

0
Jason Portnoy

Je viens de rencontrer un problème très similaire. Lors de la compilation à l'aide de Visual Studio 2010, le fichier DLL était inclus dans le dossier bin. Mais lors de la compilation à l'aide de MSBuild, le fichier tiers DLL n'était pas inclus.

Très frustrant. J'ai résolu le problème en incluant la référence NuGet au package dans mon projet Web, même si je ne l'utilise pas directement là.

0
Gary Brunton