web-dev-qa-db-fra.com

Les références de la bibliothèque de classes ne sont pas copiées dans le dossier bin du projet en cours d'exécution

J'ai une bibliothèque de classes qui représente ma couche logique. À cette bibliothèque, j'ai ajouté un paquet de nugets pour Google.Apis.Analytics.v3 - il a installé le paquet et toutes ses dépendances.

J'ai une application console qui utilise cette bibliothèque de classes logiques (référence régulière). tout est écrit et bien compilé.

Le problème est qu’au cours de l’exécution, il a généré une exception: Google.Apis.dll n’a pas été trouvé. Cette DLL est une dépendance téléchargée avec le nuget.

En vérifiant les dossiers BIN, nous avons constaté que ce DLL était présent dans le dossier bin de la bibliothèque de classes, mais qu'il ne l'était pas dans le dossier BIN de l'application console (contrairement aux autres DLL associées). Cela signifie donc que les références n’ont pas toutes été copiées lors de la compilation.

J'ai cherché en ligne et trouvé toutes sortes de solutions de contournement qui ne fonctionnaient pas vraiment (comme l'édition manuelle du fichier de projet et la suppression d'une vraie ligne xml sur cette définition de dll).

Ce que j’ai fini par faire, c’est d’ajouter la même bibliothèque de nugets à l’application de la console. Cela fonctionne mais se sent un peu sale et ne correspond pas à ce qu’il devrait être. Je pense que l'application de la console est le client qui est censé obtenir ses services de cette bibliothèque de classes logiques, qui devrait savoir que tout est en ordre sans que le "client" ne s'en préoccupe.

En outre, cette application de console n'est pas la seule à utiliser ce service, je prévois également une application Web qui utilisera cette fonctionnalité. Je devrai donc ajouter le même nuget à cette application Web. un peu brouillon ...

Est ce juste moi? est-ce la bonne façon de s'y prendre? Je pensais écrire un projet WCF pour gérer cette fonctionnalité - mais cela semble un peu lourd pour la fonctionnalité, et ralentir probablement mon flux de travail juste pour garder les choses "plus propres" à mon avis.

Est-ce que j'y réfléchis trop?

Remercier

30
developer82

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 non pour 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 pas de 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);
    }
31
deadlydog

Si vous avez la chaîne de dépendance suivante: Lib1 <- Lib2 <- MyApp

Version TLDR: en ne faisant pas de suppositions, le système de compilation évite d’introduire une incertitude/un comportement inattendu.

Lorsque vous construisez MyApp, Lib2 sera copié dans le répertoire bin de MyApp pour vous, mais Lib1 ne le sera pas. Vous devrez ajouter une référence à Lib2 et Lib1 dans MyApp afin d’obtenir les dll de Lib1 dans le répertoire bin de MyApp (sinon, vous obtiendrez une erreur d’exécution). Il serait impossible (ou peut-être simplement très difficile) d'identifier l'ensemble exact de fichiers qui se retrouveraient dans le répertoire bin de Lib2 et qu'il serait sans danger de copier sur mon application. Si le système de construction partait du principe que tout ce qui se trouvait dans le répertoire bin de Lib2 était sans danger pour MyApp ou s'il faisait implicitement référence à Lib1, cela pourrait modifier le comportement de MyApp par inadvertance.

Imaginez une solution où plus d'un projet dépend de Lib2 mais qu'un de ces projets souhaite charger un fichier .dll adjacent à l'aide de Assembly.LoadFrom/Activator.CreateInstance/MEF/etc. (un plugin) et l'autre pas. Une opération de copie automatique pourrait récupérer Lib2 avec la DLL du plug-in et la copier dans les premier et deuxième répertoires de construction du projet (car elle se trouve dans le répertoire bin de Lib2 à la suite d'une opération de construction). Cela changerait le comportement de la deuxième application.

Alternativement, s'il était un peu plus intelligent et implicitement référencé Lib1 lorsque vous avez référencé Lib2 (et ne copiez pas simplement le contenu du répertoire bin), cela pourrait quand même avoir des conséquences inattendues. Et si MyApp dépendait déjà de Lib1, mais utilisait une copie GAC'd/ngen'd compatible avec celle requise par Lib2. Si l'ajout d'une référence à Lib2 créait implicitement une référence à Lib1 pour vous, cela pourrait changer le chargement de Lib1 et le comportement d'exécution de votre application. Il pourrait peut-être détecter qu'il y a déjà une Lib1 dans le répertoire bin de MyApp et l'ignorer, mais il supposerait alors que la Lib1 qui existe déjà est la bonne. Peut-être qu’il s’agit d’un fichier .dll obsolète qui attend d’être effacé par une opération de nettoyage et le remplacement est le bon choix.

NuGet résout le problème que vous décrivez avec les dépendances de paquet. Si Lib1 et Lib2 avaient tous deux des paquets de pépites et que le paquet Lib2 dépendait du paquet Lib1, lorsque vous ajoutez Lib2 à MyApp, Lib1 serait également ajouté. Les dll des deux paquets se retrouveraient dans le répertoire bin de MyApp.

La meilleure chose à faire est d'inverser un peu votre façon de penser. Au lieu de penser: 

  • Lib2 a besoin d'une référence à Lib1 pour pouvoir compiler, je vais donc ajouter une référence à Lib1 

Pense: 

  • MyApp a besoin de Lib2. Quels que soient les besoins de Lib2, il me faut. Donc, MyApp et Lib2 obtiennent tous deux une référence à Lib1.
1
scottt732

Si vous avez 10 dlls, il est plus facile de faire une copie avec un événement postbuild:

xcopy "$(ProjectDir)bin\*.dll" $(SolutionDir)MyTargetProject\bin\" /y
0
FrankyHollywood