MSBuild kopiert keine Referenzen (DLL-Dateien), wenn Projektabhängigkeiten in der Lösung verwendet werden

273

Ich habe vier Projekte in meiner Visual Studio-Lösung (alle, die auf .NET 3.5 abzielen) - für mein Problem sind nur diese beiden wichtig:

  1. MyBaseProject <- Diese Klassenbibliothek verweist auf eine DLL-Datei eines Drittanbieters (elmah.dll).
  2. MyWebProject1 <- Dieses Webanwendungsprojekt enthält einen Verweis auf MyBaseProject

Ich habe den Verweis elmah.dll zu MyBaseProject in Visual Studio 2008 hinzugefügt, indem ich auf "Verweis hinzufügen ..." → Registerkarte "Durchsuchen" → → "elmah.dll" ausgewählt habe.

Die Eigenschaften der Elmah-Referenz sind wie folgt:

  • Aliase - global
  • Lokal kopieren - wahr
  • Kultur -
  • Beschreibung - Fehlerprotokollierungsmodule und -handler (ELMAH) für ASP.NET
  • Dateityp - Baugruppe
  • Pfad - D: \ webs \ otherfolder \ _myPath \ __ tools \ elmah \ Elmah.dll
  • Gelöst - Richtig
  • Laufzeitversion - v2.0.50727
  • Angegebene Version - falsch
  • Starker Name - falsch
  • Version - 1.0.11211.0

In MyWebProject1 habe ich den Verweis auf Project MyBaseProject hinzugefügt, indem ich: "Verweis hinzufügen ..." → Registerkarte "Projekte" → "MyBaseProject" ausgewählt habe. Die Eigenschaften dieser Referenz sind bis auf die folgenden Elemente identisch:

  • Beschreibung -
  • Pfad - D: \ webs \ CMS \ MyBaseProject \ bin \ Debug \ MyBaseProject.dll
  • Version - 1.0.0.0

Wenn ich den Build in Visual Studio ausführe, wird die Datei elmah.dll zusammen mit MyBaseProject.dll in das bin- Verzeichnis von MyWebProject1 kopiert!

Wenn ich jedoch MSBuild für die Lösung bereinige und ausführe (über D: \ webs \ CMS> C: \ WINDOWS \ Microsoft.NET \ Framework \ v3.5 \ MSBuild.exe / t: ReBuild / p: Configuration = Debug MyProject.sln ) Die Datei elmah.dll fehlt im bin-Verzeichnis von MyWebProject1 - obwohl der Build selbst keine Warnungen oder Fehler enthält!

Ich habe bereits sichergestellt, dass die .csproj von MyBaseProject das private Element mit dem Wert "true" enthält (dies sollte ein Alias ​​für " copy local " in Visual Studio sein):

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

(Das private Tag wurde standardmäßig nicht in der XML-Datei von .csproj angezeigt, obwohl Visual Studio "copy local" true sagte. Ich habe "copy local" auf "false" gesetzt - gespeichert - und es wieder auf "true" gesetzt - save!)

Was ist los mit MSBuild? Wie kann ich die Referenz (elmah.dll) in den Bin von MyWebProject1 kopieren lassen?

Ich möchte dem Postbuild-Befehl jedes Projekts KEINE Postbuild-Kopieraktion hinzufügen! (Stellen Sie sich vor, ich hätte viele Projekte, die von MyBaseProject abhängen!)

Toebens
quelle
11
Ich würde gerne eine klarere Antwort darauf bekommen, warum dies passiert.
David Faivre
1
Werfen
1
Gibt es eine endgültige Lösung mit einem vollständigen Quellcodebeispiel?
Kiquenet
2
Siehe die Antwort stackoverflow.com/a/21055664/21579 unten von @deadlydog. Hervorragende Erklärung und Lösung des Problems für mich ... Die am häufigsten gestimmte Antwort unten ist für VS2012 nicht korrekt.
Jeff Widmer

Antworten:

153

Ich bin nicht sicher, warum es beim Erstellen zwischen Visual Studio und MsBuild anders ist, aber hier ist, was ich gefunden habe, als ich auf dieses Problem in MsBuild und Visual Studio gestoßen bin.

Erläuterung

Nehmen wir für ein Beispielszenario an, wir haben Projekt X, Baugruppe A und Baugruppe B. Baugruppe A verweist auf Baugruppe B, sodass Projekt X einen Verweis auf A und B enthält. Außerdem enthält Projekt X Code, der auf Baugruppe A verweist (z. B. A. SomeFunction ()). Jetzt erstellen Sie ein neues Projekt Y, das auf Projekt X verweist.

Die Abhängigkeitskette sieht also folgendermaßen aus: Y => X => A => B.

Visual Studio / MSBuild versucht, intelligent zu sein und nur Referenzen in Projekt Y zu übertragen, die als von Projekt X erforderlich erkannt werden. Dies geschieht, um Referenzverschmutzung in Projekt Y zu vermeiden. Das Problem ist, dass VS / MSBuild nicht erkennt, dass B erforderlich ist, da Projekt X keinen Code enthält, der explizit Assembly B verwendet (z. B. B.SomeFunction ()) von X und kopiert es daher nicht in das bin-Verzeichnis von Projekt Y; Es werden nur die X- und A-Assemblys kopiert.

Lösung

Sie haben zwei Möglichkeiten, um dieses Problem zu lösen. Beide führen dazu, dass Baugruppe B in das bin-Verzeichnis von Projekt Y kopiert wird:

  1. Fügen Sie in Projekt Y einen Verweis auf Baugruppe B hinzu.
  2. Fügen Sie Dummy-Code zu einer Datei in Projekt X hinzu, die Assembly B verwendet.

Persönlich bevorzuge ich Option 2 aus mehreren Gründen.

  1. Wenn Sie in Zukunft ein anderes Projekt hinzufügen, das auf Projekt X verweist, müssen Sie nicht daran denken, auch einen Verweis auf Baugruppe B aufzunehmen (wie Sie es mit Option 1 tun müssten).
  2. Sie können explizite Kommentare haben, die angeben, warum der Dummy-Code vorhanden sein muss und nicht, um ihn zu entfernen. Wenn also jemand den Code versehentlich löscht (z. B. mit einem Refactor-Tool, das nach nicht verwendetem Code sucht), können Sie anhand der Quellcodeverwaltung leicht erkennen, dass der Code erforderlich ist, und ihn wiederherstellen. Wenn Sie Option 1 verwenden und jemand ein Refactor-Tool verwendet, um nicht verwendete Referenzen zu bereinigen, haben Sie keine Kommentare. Sie werden nur sehen, dass ein Verweis aus der .csproj-Datei entfernt wurde.

Hier ist ein Beispiel des "Dummy-Codes", den ich normalerweise hinzufüge, wenn ich auf diese Situation stoße.

    // 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);
    }
tödlicher Hund
quelle
4
Was hier nicht diskutiert wird, ist, dass es einen Unterschied zwischen dem Kopieren von Build-Produkten in / bin eines Webprojekts und dem routinemäßigen Kopieren in das Zielausgabeverzeichnis (z. B. / bin / x86 / Debug) gibt. Ersteres wird von dem referenzierten Projekt ausgeführt, wenn es erstellt wird, und letzteres wird vom abhängigen Webprojekt ausgeführt. Das Untersuchen von Microsoft.Common.targets hilft dabei, dies zu verstehen. Kopien in das Web / den Bin hängen überhaupt nicht vom lokalen Verhalten beim Kopieren ab. Kopieren Sie lokale Auswirkungen auf die Kopie in das Ausgabezielverzeichnis, das nicht Teil der Struktur ist, auf die Cassini verweist, die über Debug ausgeführt wird.
user1164178
6
Können Sie erklären, warum es mit VS funktioniert hat, OHNE diesen "Dummy-Code" hinzuzufügen, aber nicht mit msbuild?
Toebens
3
Noch weniger invasiv als das Aufrufen einer Funktion, können Sie den Typ einer in der Assembly enthaltenen Klasse einer Dummy-Variablen zuweisen. Type dummyType = typeof(AssemblyA.AnyClass);
Arithmomaniac
14
Lösung 2 wie oben gezeigt funktioniert nur, wenn Sie die Einstellung "Code optimieren" in Visual Studio aktiviert haben. In diesem Fall wird die DLL weiterhin ausgeschlossen. Ich habe eine weitere Zeile hinzugefügt, um die "Optimierung" zu überschreiben. Console.WriteLine(dummyType.FullName);
JasonG
3
Lösung 2 funktionierte in Visual Studio 2017 nicht für mich. Ich dachte zuerst, dass dies daran lag, dass ich in meiner Assembly X nur ein enumvon B verwendete und davon ausging, dass die Aufzählung inline ist. Ich habe Code hinzugefügt, um einen Typ von B direkt zu verwenden, und das hat nicht geholfen. Es war auch immer so, dass mein X Typen in A verwendet, die Unterklassentypen in B sind, so dass ich nicht verstehen kann, wie der Compiler denkt, dass B von X nicht benötigt wird und ignoriert werden kann. Das sind Trottel.
Xharlie
170

Ich gehe einfach so damit um. Gehen Sie zu den Eigenschaften Ihrer Referenz und gehen Sie folgendermaßen vor:

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

und das ist es.

In Visual Studio 2010 wird zunächst nicht Folgendes eingefügt: <private>True</private> Wenn Sie das Referenz-Tag in das Referenz-Tag setzen und "copy local" auf "false" setzen, wird das Tag erstellt. Danach wird es entsprechend auf wahr und falsch gesetzt.

Andrew
quelle
10
Dies war ein Glücksfall. Danke dafür!
Rebecca
22
Hat bei mir mit MSBuild 4 / VS2012 nicht funktioniert. Das heißt, ich konnte Referenzen aktualisieren, um zu sagen, <Private>true</Private>aber es schien keine Auswirkung auf MSBuild zu haben. Am Ende habe ich nur NuGet-Referenzen zu Downlevel-Projekten hinzugefügt.
Michael Teper
2
Hat bei mir nicht funktioniert. System.Net.Http.Formatting wird immer noch nicht in den Ordner bin kopiert.
Akira Yamamoto
4
Dies ist das Äquivalent zu Have you tried turning it off and on again?und es hat funktioniert!
Guanome
6
Es sieht so aus, als ob sich VS2015 immer noch genauso verhält: Das Setzen von 'Copy Local' auf 'False' und dann zurück auf 'True' bei referenzierten DLLs funktioniert.
Flip
38

Wenn Sie die Assembly nicht direkt im Code verwenden, erkennt Visual Studio beim Versuch, hilfreich zu sein, dass sie nicht verwendet wird, und nimmt sie nicht in die Ausgabe auf. Ich bin nicht sicher, warum zwischen Visual Studio und MSBuild ein unterschiedliches Verhalten auftritt. Sie können versuchen, die Build-Ausgabe für beide auf Diagnose zu setzen und die Ergebnisse zu vergleichen, um festzustellen, wo sie abweichen.

Wenn Sie Ihre elmah.dll-Referenz nicht direkt im Code referenzieren, können Sie sie als Element zu Ihrem Projekt hinzufügen und die Build-Aktion auf Contentund das Kopieren in Ausgabeverzeichnis auf setzen Always.

John Hunter
quelle
3
+1 für Ihre Kopie in das Ausgabeverzeichnis Kommentar, wenn Elmah nicht im Code verwendet wird, ist es sinnvoll, als Inhalt zu kopieren.
Kit Roed
4
In der Tat werden nicht verwendete Assemblys ignoriert, ABER eine wichtige Sache, die zu beachten ist: Ab VS 2010 wird die Verwendung von Assemblys in XAML-Ressourcenwörterbüchern von VS nicht als assmebly verwendet, sodass sie nicht kopiert werden.
Alex Burtsev
Dies ist besser für ein Unit-Test-Projekt, bei dem ich die DLL benötige. Ich möchte keine DLL hinzufügen, die das Hauptprojekt nicht nur benötigt, um die Tests auszuführen!
Lukos
14

Schauen Sie sich an:

Diesen MSBuild-Forenthread habe ich gestartet

Dort finden Sie meine vorübergehende Lösung / Problemumgehung!

(MyBaseProject benötigt Code, der auf einige Klassen (was auch immer) aus der elmah.dll verweist, damit elmah.dll in den Bin von MyWebProject1 kopiert wird!)

Toebens
quelle
1
Verdammt - dies ist die einzige Lösung, zu der ich auch gekommen war - hoffte, dass es einen besseren Weg gibt, es zu tun!
Nickspoon
2
Siehe Andrews Antwort unten für eine weniger hackige Lösung (keine Beleidigung!)
MPritchard
1
Für diejenigen, die sich das jetzt ansehen, ist die Antwort von toebens auf MSDN im Wesentlichen dieselbe wie die Antwort von tödlichem Hund , die einige Jahre später gegeben wurde.
Jpaugh
8

Ich hatte das gleiche Problem.

Überprüfen Sie, ob die Framework-Version Ihres Projekts mit der Framework-Version der DLL übereinstimmt, auf die Sie verweisen.

In meinem Fall wurde mein Client mit "Framework 4 Client" kompiliert und die DLL befand sich in "Framework 4".

Andre Avelar
quelle
6

Das Problem, mit dem ich konfrontiert war, war, dass ich ein Projekt habe, das von einem Bibliotheksprojekt abhängig ist. Um zu bauen, habe ich folgende Schritte ausgeführt:

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

Das bedeutete natürlich, dass mir die DLL-Dateien meiner Bibliothek in bin und vor allem in der Paket-Zip-Datei fehlten. Ich fand das funktioniert perfekt:

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

Ich habe keine Ahnung, warum diese Arbeit oder warum es überhaupt nicht funktioniert hat. Aber hoffe das hilft.

vangorra
quelle
Ich hatte das gleiche Problem beim Erstellen der gesamten Lösung mit / t: Build aus TeamCity in einem Schritt und dann im nächsten / t: Package on WebAPI-Projekt. Alle DLLs, auf die durch Projektreferenzen verwiesen wird, wurden nicht berücksichtigt. Dies wurde mit dem obigen - / T: Rebuild; -Paket auf der WebAPI behoben, das dann diese DLLs enthielt.
Andy Hoyle
5

Ich hatte gerade genau das gleiche Problem und es stellte sich heraus, dass 2 Projekte in derselben Lösung auf eine andere Version der Bibliothek eines Drittanbieters verweisen.

Nachdem ich alle Referenzen korrigiert hatte, funktionierte alles perfekt.

Steven Engels
quelle
4

Wie Alex Burtsev in einem Kommentar erwähnt hat, wird alles, was nur in einem XAML-Ressourcenwörterbuch verwendet wird, oder in meinem Fall alles, was nur in XAML und nicht in Code dahinter verwendet wird, von MSBuild nicht als "in Verwendung" angesehen.

Das einfache Neuerstellen eines Dummy-Verweises auf eine Klasse / Komponente in der Assembly in einem Code dahinter hat MSBuild davon überzeugt, dass die Assembly tatsächlich verwendet wird.

Scott
quelle
Dies war genau mein Problem und die Lösung, die funktionierte. Ich habe zu lange versucht, das herauszufinden. Vielen Dank Scott & @Alex Burstev
Karol
Meine Güte, das hat mich verrückt gemacht. Ja , ich habe FontAwesome.WPF verwendet und nur innerhalb der XAML (aus offensichtlichen Gründen). Das Hinzufügen einer Dummy-Methode hat geholfen. Vielen Dank! Und ja, VS 2017 15.6 ist immer noch betroffen, also habe ich einen Fehler
gemeldet
3

Ändern des Zielframeworks von .NET Framework 4-Clientprofil zu .NET Framework 4 dieses Problem für mich behoben.

In Ihrem Beispiel: Setzen Sie das Zielframework in MyWebProject1 auf .NET Framework 4

Fuyu Persimmon
quelle
3

Mit dem Schema von Deaddog,

Y => X => A => B. ,

Mein Problem war, als ich Y erstellte, dass die Assemblys (A und B, alle 15) von X nicht im Ordner bin von Y angezeigt wurden.

Ich habe es gelöst, indem ich die Referenz X aus Y entfernt, gespeichert, erstellt, dann die X-Referenz (eine Projektreferenz) erneut hinzugefügt und gespeichert, erstellt habe und A und B im Ordner bin von Y angezeigt wurden.

JZ
quelle
Nachdem ich viele Stunden lang viele andere Lösungen gesucht und ausprobiert hatte, funktionierte diese für mich.
Suncat2000
2

Ich hatte das gleiche Problem und die DLL war eine dynamisch geladene Referenz. Um das Problem zu lösen, habe ich ein "using" mit dem Namespace der DLL hinzugefügt. Jetzt wird die DLL in den Ausgabeordner kopiert.

Spamme
quelle
2

Dazu müssen Sie .targetsIhrem Projekt eine Datei hinzufügen und festlegen, dass sie in den Includes-Abschnitt des Projekts aufgenommen wird.

Sehen meine Antwort hier für das Verfahren.

Alex Essilfie
quelle
2

Das Referenzieren von Assemblys, die während des Builds nicht verwendet werden, ist nicht die richtige Vorgehensweise. Sie sollten Ihre Build-Datei erweitern, damit die zusätzlichen Dateien kopiert werden. Entweder mithilfe eines Post-Build-Ereignisses oder durch Aktualisieren der Eigenschaftsgruppe.

Einige Beispiele finden Sie in einem anderen Beitrag

verbedr
quelle
2

Ein anderes Szenario, in dem dies angezeigt wird, ist, wenn Sie den älteren Projekttyp "Website" in Visual Studio verwenden. Für diesen Projekttyp kann es nicht auf DLLs verweisen, die sich außerhalb der eigenen Verzeichnisstruktur befinden (aktueller Ordner und nach unten). Nehmen wir in der obigen Antwort an, Ihre Verzeichnisstruktur sieht folgendermaßen aus:

Geben Sie hier die Bildbeschreibung ein

Wenn ProjectX und ProjectY übergeordnete / untergeordnete Verzeichnisse sind und ProjectX auf A.dll verweist, die wiederum auf B.dll verweist, und B.dll außerhalb der Verzeichnisstruktur liegt, z. B. in einem Nuget-Paket im Stammverzeichnis (Packages), dann auf A. DLL wird enthalten sein, B. DLL jedoch nicht.

Focker
quelle
0

Ich hatte heute ein ähnliches Problem, und dies ist mit Sicherheit nicht die Antwort auf Ihre Frage. Aber ich möchte alle informieren und möglicherweise einen Funken Einsicht geben.

Ich habe eine ASP.NET-Anwendung. Der Erstellungsprozess wird auf Bereinigen und dann Erstellen festgelegt.

Ich habe zwei Jenkins CI- Skripte. Eine für die Produktion und eine für die Inszenierung. Ich habe meine Anwendung für das Staging bereitgestellt und alles hat einwandfrei funktioniert. Wird in der Produktion bereitgestellt und es fehlte eine DLL-Datei, auf die verwiesen wurde. Diese DLL-Datei befand sich nur im Stammverzeichnis des Projekts. Nicht in einem NuGet-Repository. Die DLL wurde auf gesetzt do not copy.

Das CI-Skript und die Anwendung waren zwischen den beiden Bereitstellungen identisch. Nach dem Bereinigen und Bereitstellen in der Staging-Umgebung wurde die DLL-Datei am Bereitstellungsort der ASP.NET-Anwendung ( bin/) ersetzt. Dies war in der Produktionsumgebung nicht der Fall.

Es stellt sich heraus, dass ich in einem Testzweig dem Erstellungsprozess einen Schritt hinzugefügt habe, um diese DLL-Datei in die zu kopieren bin Verzeichnis . Nun der Teil, der eine Weile gedauert hat, um herauszufinden. Der CI-Prozess reinigte sich nicht von selbst. Die DLL wurde im Arbeitsverzeichnis belassen und versehentlich mit der ASP.NET-ZIP-Datei gepackt. Der Produktionszweig hatte die DLL-Datei nie auf die gleiche Weise kopiert und stellte diese niemals versehentlich bereit.

TLDR; Überprüfen Sie, ob Sie wissen, was Ihr Build-Server tut.

Jason Portnoy
quelle
0

Stellen Sie sicher, dass sich beide Projekte in derselben .net-Version befinden. Überprüfen Sie auch die lokale Eigenschaft zum Kopieren. Dies sollte jedoch truedie Standardeinstellung sein

john.kernel
quelle
0

Verwenden von Visual Studio 2015 Hinzufügen des zusätzlichen Parameters

/ deployonbuild = false

In der msbuild-Befehlszeile wurde das Problem behoben.

Giles Roberts
quelle
-1

Ich bin gerade auf ein sehr ähnliches Problem gestoßen. Beim Kompilieren mit Visual Studio 2010 wurde die DLL-Datei in den binOrdner aufgenommen. Beim Kompilieren mit MSBuild wurde die DLL-Datei eines Drittanbieters jedoch nicht berücksichtigt.

Sehr frustrierend. Die Art und Weise, wie ich es gelöst habe, bestand darin, den NuGet- Verweis auf das Paket in mein Webprojekt aufzunehmen, obwohl ich es dort nicht direkt verwende.

Gary Brunton
quelle
Dies ist ein Duplikat der Top-Antwort.
Giles Roberts
-3

Das Einfügen aller referenzierten DLL-Dateien aus Ihren Projektreferenzen in das Website-Projekt ist nicht immer eine gute Idee, insbesondere wenn Sie die Abhängigkeitsinjektion verwenden : Ihr Webprojekt möchte lediglich einen Verweis auf die DLL-Datei / das Projekt der Schnittstelle hinzufügen, keine konkrete Implementierungs-DLL Datei.

Wenn Sie einen Verweis direkt auf eine Implementierungs-DLL-Datei / ein Implementierungs-DLL-Projekt hinzufügen, können Sie Ihren Entwickler nicht daran hindern, für konkrete Klassen der Implementierungs-DLL-Datei / des Implementierungs-DLL-Projekts ein "neues" aufzurufen, anstatt über die Schnittstelle. Außerdem haben Sie auf Ihrer Website einen "Hardcode" angegeben, um die Implementierung zu verwenden.

Hung Nguyen
quelle