MSBuild ne copie pas de références (fichiers DLL) si vous utilisez des dépendances de projet 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 éléments sont importants:

  1. MyBaseProject <- cette bibliothèque de classes référence un fichier DLL tiers (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
  • Copie locale – true
  • Culture –
  • Description – Modules et gestionnaires de journalisation des erreurs (ELMAH) pour ASP.NET
  • Type de fichier – Assemblage
  • Chemin d’access – 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 par: “Ajouter une référence …” → onglet “Projets” → en sélectionnant “MyBaseProject”. Les propriétés de cette référence sont les mêmes sauf les membres suivants:

  • La description –
  • Chemin d’access – D: \ webs \ CMS \ MyBaseProject \ bin \ Debug \ MyBaseProject.dll
  • Version – 1.0.0.0

Si je lance la construction dans Visual Studio, le fichier elmah.dll est copié dans le répertoire bin de mon 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 = Debug MyProject.sln ) le fichier elmah.dll est manquant dans le répertoire bin de MyWebProject1 – bien que la version elle-même ne contienne aucun avertissement ou erreur!

Je me suis déjà assuré que le fichier .csproj de MyBaseProject contient l’élément privé avec la valeur “true” (cela devrait être un alias pour ” copy local ” dans Visual Studio):

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

(La balise privée n’apparaissait pas par défaut dans le fichier XML du fichier .csproj, bien que Visual Studio ait dit “copie locale” true. J’ai remplacé “copy local” par false – sauvegardé – et le réinitialise à nouveau – save!)

Quel est le problème avec MSBuild? Comment puis-je obtenir la référence (elmah.dll) copiée dans la corbeille de MyWebProject1?

Je ne veux PAS append une action de copie postbuild à la commande postbuild de chaque projet! (Imaginez que de nombreux projets dépendaient de MyBaseProject!)

Je ne suis pas sûr pourquoi il est différent lors de la construction 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’assembly A et l’assembly B. Assembly Un ensemble de références B, ainsi le projet X inclut une référence à la fois à A et B. Le projet X inclut également le code qui référence l’assembly A SomeFunction ()). Maintenant, vous créez 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 ne renvoie que les références dans le projet Y qu’il détecte comme étant requirejs par le projet X; cela permet d’éviter la pollution de référence dans le projet Y. Le problème est que, puisque le projet X ne contient aucun code qui utilise explicitement l’assemblage B (par exemple B.SomeFunction ()), VS / MSBuild ne détecte pas que B est requirejs par X, et ne le copie donc pas dans le répertoire bin du projet Y; il ne fait que copier les assemblages X et A.

Solution

Vous avez deux options pour résoudre ce problème, les deux résultant en une copie de l’assemblage B dans le répertoire bin du projet Y:

  1. Ajoutez une référence à l’assemblage B dans le projet Y.
  2. Ajouter un code factice à un fichier dans le projet X qui utilise l’assembly B.

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

  1. Si vous ajoutez à l’avenir un autre projet faisant référence au projet X, vous n’aurez pas à vous souvenir d’inclure également une référence à l’assemblage B (comme vous le feriez avec l’option 1).
  2. Vous pouvez avoir des commentaires explicites expliquant pourquoi le code factice doit être présent et ne pas le supprimer. Donc, si quelqu’un supprime le code par accident (par exemple, avec un outil de refactoring qui recherche le code inutilisé), vous pouvez facilement voir dans le contrôle de code source que le code est requirejs 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 simplement 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); } 

Je m’en occupe 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’inclut pas initialement: True dans la balise de référence et la définition de “copy local” sur false provoque la création de la balise. Après cela, il sera mis à vrai et faux en conséquence.

Si vous n’utilisez pas l’assemblage directement dans le code, Visual Studio, tout en essayant d’être utile, détecte qu’il n’est pas utilisé et ne l’inclut pas dans la sortie. Je ne sais pas pourquoi vous voyez un comportement différent entre Visual Studio et MSBuild. Vous pouvez essayer de paramétrer la sortie de build pour un diagnostic à la fois et comparer les résultats voir où elle diffère.

En ce qui concerne votre référence elmah.dll si vous ne la référencez pas directement dans le code, vous pouvez l’append en tant qu’élément à votre projet et définir l’action de création sur le Content et le répertoire de copie sur sortie sur Always .

Jeter un coup d’œil à:

Ce sujet de forum MSBuild j’ai commencé

Vous trouverez ma solution / solution temporaire là-bas!

(MyBaseProject a besoin d’un code référençant certaines classes (peu importe) à partir de elmah.dll pour que elmah.dll soit copié dans le répertoire de MyWebProject1!)

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

Vérifiez si la version du framework de votre projet est la même que celle du framework que vous avez mis en référence.

Dans mon cas, mon client a été compilé en utilisant “Framework 4 Client” et la DLL était dans “Framework 4”.

Le problème auquel j’étais confronté était que mon projet dépendait 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 que je manquais les fichiers dll de ma bibliothèque dans bin et surtout dans le fichier zip du paquet. J’ai trouvé que cela fonctionne parfaitement:

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

Je n’ai aucune idée de la raison de ce travail ou de son absence. Mais j’espère que ça aide.

Comme Alex Burtsev a mentionné dans un commentaire tout ce qui n’est utilisé que dans un dictionnaire de ressources XAML ou, dans mon cas, tout ce qui n’est utilisé que dans XAML et non dans le code, n’est pas considéré comme étant «utilisé» par MSBuild.

Donc, le fait de simplement inventer une référence factice à une classe / un composant dans l’assembly dans un code derrière était assez convaincre que MSBuild était effectivement en train d’utiliser l’assembly.

La modification de la structure cible de .NET Framework 4 Client Profile à .NET Framework 4 a résolu ce problème pour moi.

Donc, dans votre exemple: définissez le framework cible sur MyWebProject1 à .NET Framework 4

J’ai eu exactement le même problème et il s’est avéré que deux projets de la même solution faisaient référence à une version différente de la bibliothèque tierce.

Une fois toutes les références corrigées, tout fonctionnait parfaitement.

J’ai eu le même problème et le dll était une référence chargée dynamicment. Pour résoudre le problème, j’ai ajouté un “using” avec l’espace de nom de la DLL. Maintenant, la DLL est copiée dans le dossier de sortie.

Cela nécessite d’append un fichier .targets à votre projet et de le définir pour qu’il soit inclus dans la section .targets du projet.

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

Le référencement des assemblys non utilisés lors de la construction n’est pas la bonne pratique. Vous devez augmenter votre fichier de compilation pour qu’il copie les fichiers supplémentaires. Soit en utilisant un événement de post-construction ou en mettant à jour le groupe de propriétés.

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

  • MSBuild pour copier des fichiers générés dynamicment dans le cadre de la dépendance du projet
  • VS2010 Comment inclure des fichiers dans un projet, pour les copier pour créer automatiquement un répertoire de sortie pendant la génération ou la publication

Utiliser le schéma de Deadlydog,

Y => X => A => B ,

Mon problème était que lorsque j’ai construit Y, les assemblages (A et B, tous les 15) 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 compilant, puis en rajoutant la référence X (une référence de projet) et en sauvegardant, construisant et A et B apparaissant dans le dossier bin de Y.

Un autre scénario où cela apparaît est si vous utilisez l’ancien type de projet “Web Site” dans Visual Studio. 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 et fichiers en cours). Donc, dans la réponse ci-dessus, supposons que votre structure de répertoire ressemble à ceci:

entrer la description de l'image ici

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épertoire, comme dans un package Nuget à la racine (Packages), puis A. dll sera inclus, mais B.dll ne sera pas.

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 DLL tiers n’était pas inclus.

Très frustrant. La façon dont je l’ai résolu était d’inclure la référence de NuGet au paquet dans mon projet Web même si je ne l’utilise pas directement là-bas.

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 peut-être fournir une étincelle de perspicacité.

J’ai une application ASP.NET. Le processus de génération est défini pour nettoyer et générer.

J’ai deux scripts Jenkins CI . Un pour la production et un pour la mise en scène. J’ai déployé mon application pour le staging et tout s’est bien passé. Déploiement en production et absence d’un fichier DLL référencé. Ce fichier DLL était juste à la racine du projet. Pas dans un repository NuGet. La DLL a été définie pour 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é dans l’emplacement de déploiement de l’application ASP.NET ( bin/ ). Ce n’était pas le cas pour l’environnement de production.

Il s’est avéré que dans une twig de test j’avais ajouté une étape au processus de compilation pour copier ce fichier DLL dans le répertoire bin . Maintenant, la partie qui a pris un peu de temps pour comprendre. Le processus CI ne nettoyait pas lui-même. La DLL est restée dans le répertoire de travail et était accidentellement empaquetée avec le fichier ASP.NET .zip. La twig de production n’a jamais eu le fichier DLL copié de la même manière et ne l’a jamais déployé accidentellement.

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

L’inclusion de tous les fichiers DLL référencés de vos références de projet dans le projet de site Web n’est pas toujours une bonne idée, surtout lorsque vous utilisez l’ dependency injection : votre projet Web veut simplement append une référence au fichier / projet DLL d’interface, pas de DLL d’implémentation concrète fichier.

Parce que si vous ajoutez une référence directement à un fichier / projet DLL d’implémentation, vous ne pouvez pas empêcher votre développeur d’appeler un “nouveau” sur des classes concrètes du fichier / projet DLL d’implémentation plutôt que via l’interface. C’est aussi vous qui avez indiqué un “code dur” sur votre site Web pour utiliser l’implémentation.