MSBuild non copia i riferimenti (file DLL) se si utilizzano le dipendenze del progetto nella soluzione


273

Ho quattro progetti nella mia soluzione Visual Studio (tutti rivolti a .NET 3.5) - per il mio problema solo questi due sono importanti:

  1. MyBaseProject <: questa libreria di classi fa riferimento a un file DLL di terze parti (elmah.dll)
  2. MyWebProject1 <: questo progetto di applicazione Web ha un riferimento a MyBaseProject

Ho aggiunto il riferimento elmah.dll a MyBaseProject in Visual Studio 2008 facendo clic su "Aggiungi riferimento ..." → scheda "Sfoglia" → selezionando "elmah.dll".

Le proprietà del riferimento Elmah sono le seguenti:

  • Alias ​​- globale
  • Copia local - true
  • Cultura -
  • Descrizione - Moduli e gestori di registrazione errori (ELMAH) per ASP.NET
  • Tipo di file - Assembly
  • Percorso - D: \ webs \ otherfolder \ _myPath \ __ tools \ elmah \ Elmah.dll
  • Risolto - Vero
  • Versione runtime - v2.0.50727
  • Versione specificata - false
  • Nome sicuro: falso
  • Versione - 1.0.11211.0

In MyWebProject1 ho aggiunto il riferimento a Project MyBaseProject da: "Aggiungi riferimento ..." → scheda "Progetti" → selezionando "MyBaseProject". Le proprietà di questo riferimento sono le stesse tranne i seguenti membri:

  • Descrizione -
  • Percorso - D: \ webs \ CMS \ MyBaseProject \ bin \ Debug \ MyBaseProject.dll
  • Versione - 1.0.0.0

Se eseguo la build in Visual Studio, il file elmah.dll viene copiato nella directory bin del mio MyWebProject1 , insieme a MyBaseProject.dll!

Tuttavia, se pulisco ed eseguo MSBuild per la soluzione (tramite D: \ webs \ CMS> C: \ WINDOWS \ Microsoft.NET \ Framework \ v3.5 \ MSBuild.exe / t: ReBuild / p: Configuration = Debug MyProject.sln ) il file elmah.dll manca nella directory bin di MyWebProject1, sebbene la build stessa non contenga alcun avviso o errore!

Ho già fatto in modo che .csproj di MyBaseProject contenga l' elemento privato con il valore "true" (che dovrebbe essere un alias per " copia locale " in 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>

(Il tag privato non appariva nell'XML di .csproj per impostazione predefinita, anche se Visual Studio diceva "copia locale" vero. Ho cambiato "copia locale" in falso - salvato - e lo ho impostato di nuovo su vero - salva!)

Cosa c'è di sbagliato in MSBuild? Come ottengo il riferimento (elmah.dll) copiato nel cestino di MyWebProject1?

NON voglio aggiungere un'azione di copia postbuild al comando postbuild di ogni progetto! (Immagina che molti progetti dipendano da MyBaseProject!)


11
Mi piacerebbe avere una risposta più chiara sul perché questo accada.
David Faivre,

1
Dai un'occhiata alla risposta fornita qui
Anuroopa Shenoy,

1
qualche soluzione finale con un esempio di codice sorgente completo che ci sta lavorando?
Kiquenet,

2
Vedere la risposta stackoverflow.com/a/21055664/21579 seguito da @deadlydog. Ottima spiegazione e risolto il problema per me ... la risposta più votata di seguito non è corretta per VS2012.
Jeff Widmer,

Risposte:


153

Non sono sicuro del perché sia ​​diverso quando si crea tra Visual Studio e MsBuild, ma ecco cosa ho trovato quando ho riscontrato questo problema in MsBuild e Visual Studio.

Spiegazione

Per uno scenario di esempio, supponiamo di avere il progetto X, l'assemblaggio A e l'assemblaggio B. L'assemblaggio A fa riferimento all'assemblaggio B, quindi il progetto X include un riferimento sia a A che B. Inoltre, il progetto X include il codice che fa riferimento all'assemblaggio A (ad es. A. SomeFunction ()). Ora crei un nuovo progetto Y che fa riferimento al progetto X.

Quindi la catena di dipendenza si presenta così: Y => X => A => B

Visual Studio / MSBuild cerca di essere intelligente e porta solo riferimenti nel progetto Y che rileva come richiesto dal progetto X; lo fa per evitare l'inquinamento di riferimento nel progetto Y. Il problema è che, poiché il progetto X in realtà non contiene alcun codice che utilizza esplicitamente l'assemblaggio B (ad es. B.SomeFunction ()), VS / MSBuild non rileva la necessità di B da X, e quindi non lo copia nella directory bin del progetto Y; copia solo gli assiemi X e A.

Soluzione

Sono disponibili due opzioni per risolvere questo problema, entrambe le quali comporteranno la copia dell'assembly B nella directory bin del progetto Y:

  1. Aggiungere un riferimento all'assieme B nel progetto Y.
  2. Aggiungere il codice fittizio a un file nel progetto X che utilizza l'assembly B.

Personalmente preferisco l'opzione 2 per un paio di motivi.

  1. Se in futuro aggiungi un altro progetto che fa riferimento al progetto X, non dovrai ricordare di includere anche un riferimento all'assembly B (come avresti a che fare con l'opzione 1).
  2. Puoi avere commenti espliciti che spiegano perché il codice fittizio deve essere presente e non rimuoverlo. Quindi, se qualcuno elimina il codice per errore (diciamo con uno strumento refactor che cerca codice inutilizzato), puoi facilmente vedere dal controllo del codice sorgente che il codice è necessario e ripristinarlo. Se usi l'opzione 1 e qualcuno usa uno strumento refactor per ripulire i riferimenti inutilizzati, non hai commenti; vedrai che un riferimento è stato rimosso dal file .csproj.

Ecco un esempio del "codice fittizio" che in genere aggiungo quando incontro questa situazione.

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

4
Ciò che non viene discusso qui è che esiste una differenza tra la copia dei prodotti di compilazione in / bin di un progetto Web e la copia di routine nella directory di output di destinazione (ad esempio / bin / x86 / Debug). Il primo viene eseguito dal progetto di riferimento quando viene creato e il secondo dal progetto Web dipendente. Esaminare Microsoft.Common.targets aiuta a capire questo. Le copie sul web / bin non dipendono affatto dal comportamento locale di copia: copiare gli impatti locali sulla copia nella directory di destinazione dell'output che non fa parte della struttura a cui fa riferimento Cassini in esecuzione tramite Debug.
user1164178

6
puoi spiegare perché ha funzionato con VS WITHOUT aggiungendo quel "codice fittizio" ma non con msbuild?
toebens,

3
Ancora meno invasivo rispetto al richiamo di una funzione, è possibile assegnare il tipo di una classe contenuta all'interno dell'assembly a una variabile fittizia. Type dummyType = typeof(AssemblyA.AnyClass);
Arithmomaniac

14
La soluzione n. 2 come mostrato sopra funzionerà a meno che non sia selezionata l'impostazione "Ottimizza codice" in Visual Studio. In tal caso, escluderà comunque la dll. Ho aggiunto un'altra riga per sovrascrivere la sua "ottimizzazione". Console.WriteLine(dummyType.FullName);
JasonG,

3
La soluzione 2 non ha funzionato per me in Visual Studio 2017. Ho pensato per la prima volta perché, nel mio assembly X, stavo usando solo un enumda B e ho pensato che l'enum fosse stato incorporato. Ho aggiunto il codice per utilizzare direttamente un tipo da B e questo non ha aiutato. È sempre stato anche il caso in cui la mia X utilizza tipi in A che subclassano i tipi in B, quindi non riesco a capire come il compilatore pensi che B non sia richiesto da X e possa essere ignorato. Questo è un disastro.
Xharlie,

170

Mi occupo solo di questo. Vai alle proprietà del tuo riferimento e fai questo:

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

e basta.

Visual Studio 2010 inizialmente non inserisce: <private>True</private> nel tag di riferimento e l'impostazione di "copia locale" su false provoca la creazione del tag. Successivamente lo imposterà su true e false di conseguenza.


10
Questa era una manna dal cielo. Grazie per questo!
Rebecca,

22
Non ha funzionato per me con MSBuild 4 / VS2012. Cioè, sono stato in grado di aggiornare i riferimenti per dire, <Private>true</Private>ma sembrava non avere alcun effetto su MSBuild. Alla fine, ho appena aggiunto i riferimenti NuGet ai progetti di livello inferiore.
Michael Teper,

2
Non ha funzionato per me. Non copia ancora System.Net.Http.Formatting nella cartella bin.
Akira Yamamoto,

4
Questo è l'equivalente di Have you tried turning it off and on again?, e ha funzionato!
guanome,

6
Sembra che VS2015 si comporti ancora allo stesso modo: l'impostazione di "Copia locale" su "Falso", quindi di nuovo su "Vero" su .dll di riferimento, funziona.
capovolgi il

38

Se non si utilizza l'assembly direttamente nel codice, Visual Studio mentre cerca di essere utile rileva che non viene utilizzato e non lo include nell'output. Non sono sicuro del motivo per cui stai riscontrando comportamenti diversi tra Visual Studio e MSBuild. Potresti provare a impostare l'output di build su diagnostico per entrambi e confrontare i risultati per vedere dove differisce.

Per quanto riguarda il riferimento a elmah.dll se non lo si fa direttamente riferimento nel codice, è possibile aggiungerlo come elemento al progetto e impostare l'azione di generazione su Contente Copia su directory di output su Always.


3
+1 per la tua copia nel commento della directory di output, se Elmah non viene utilizzato nel codice, ha senso copiarlo come contenuto.
Kit Roed,

4
In effetti, ignora gli assembly che non vengono utilizzati, MA una cosa importante da notare, a partire da VS 2010 che utilizza assembly nei dizionari di risorse XAML non è considerato come un utilizzo assimilabile da VS, quindi non lo copia.
Alex Burtsev,


Questo è meglio per un progetto di unit test in cui ho bisogno della dll. Non voglio aggiungere una DLL che il progetto principale non ha bisogno solo per eseguire i test!
Lukos,

14

Date un'occhiata al:

Ho iniziato questo thread del forum MSBuild

Lì troverai la mia soluzione / soluzione temporanea!

(MyBaseProject necessita di un codice che faccia riferimento ad alcune classi (qualunque cosa) da elmah.dll per elmah.dll da copiare nel cestino di MyWebProject1!)


1
Accidenti - questa è l'unica soluzione a cui sono arrivato anch'io - speravo che ci potesse essere un modo migliore per farlo!
nickspoon

2
Vedi la risposta di andrew sotto per una soluzione meno confusa (senza offesa!)
MPritchard

1
Per coloro che la stanno esaminando ora, la risposta di toebens su MSDN è essenzialmente la stessa di quella di un cane mortale , fornita alcuni anni dopo.
jpaugh

8

Ho avuto lo stesso problema.

Verifica se la versione del framework del tuo progetto è la stessa della versione del framework della dll che hai inserito come riferimento.

Nel mio caso, il mio client è stato compilato usando "Framework 4 Client" e la DLL era in "Framework 4".


6

Il problema che stavo affrontando era che ho un progetto che dipende da un progetto di biblioteca. Per costruire stavo seguendo questi passaggi:

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

Ciò ovviamente significava che mi mancavano i file DLL della mia libreria nel cestino e, soprattutto, nel file zip del pacchetto. Ho trovato che funziona perfettamente:

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

Non ho idea del perché questo lavoro o perché non abbia funzionato. Ma spero che sia d'aiuto.


Ho avuto lo stesso problema nel creare l'intera soluzione usando / t: Build da TeamCity in un passaggio, quindi nel successivo / t: pacchetto sul progetto WebAPI. Le dll a cui fa riferimento qualsiasi riferimento al progetto non sono state incluse. Questo è stato risolto usando il precedente - / T: Rebuild; Il pacchetto sulla WebAPI includeva quindi quelle dll.
Andy Hoyle,

5

Ho appena avuto lo stesso identico problema e si è scoperto che il fatto che 2 progetti nella stessa soluzione facessero riferimento a una versione diversa della libreria di terze parti.

Dopo aver corretto tutti i riferimenti, tutto ha funzionato perfettamente.


4

Come Alex Burtsev ha menzionato in un commento tutto ciò che è utilizzato solo in un dizionario delle risorse XAML, o nel mio caso, tutto ciò che è utilizzato solo in XAML e non nel codice sottostante, non è considerato "in uso" da MSBuild.

Quindi semplicemente aggiungere un riferimento fittizio a una classe / componente nell'assembly in un po 'di codice è stato sufficiente a convincere MSBuild che l'assemblaggio era effettivamente in uso.


Questo era esattamente il mio problema e la soluzione che ha funzionato. Trascorso troppo tempo cercando di capirlo. Grazie Scott & @Alex Burstev
karol,

Buon dolore, questo mi stava facendo impazzire. , stavo usando FontAwesome.WPF e solo dall'interno di XAML (per ovvi motivi). Aggiunta di un metodo fittizio aiutato. Grazie! E sì, VS 2017 15.6 è ancora interessato, quindi ho archiviato un bug: github.com/dotnet/roslyn/issues/25349
Sören Kuklau

3

La modifica del framework di destinazione da .NET Framework 4 Client Profile a .NET Framework 4 ha risolto questo problema per me.

Quindi nel tuo esempio: imposta il framework di destinazione su MyWebProject1 su .NET Framework 4


3

Usando lo schema del cane mortale,

Y => X => A => B ,

il mio problema era quando costruivo Y, gli assembly (A e B, tutti e 15) di X non venivano visualizzati nella cartella bin di Y.

L'ho risolto rimuovendo il riferimento X da Y, salvando, costruendo, quindi aggiungendo nuovamente il riferimento X (un riferimento al progetto) e salvando, costruendo e A e B hanno iniziato a comparire nella cartella bin di Y.


Dopo molte ore di ricerca e di prova di molte altre soluzioni, questa ha funzionato per me.
Suncat2000,

2

Ho avuto lo stesso problema e la dll era un riferimento caricato dinamicamente. Per risolvere il problema ho aggiunto un "utilizzo" con lo spazio dei nomi della dll. Ora la dll viene copiata nella cartella di output.


2

Ciò richiede l'aggiunta di un .targetsfile al progetto e l'impostazione per includerlo nella sezione include del progetto.

Vedi la mia risposta qui per la procedura.


2

Fare riferimento agli assiemi che non vengono utilizzati durante la compilazione non è la pratica corretta. È necessario aumentare il file di build in modo che copierà i file aggiuntivi. O utilizzando un evento post build o aggiornando il gruppo di proprietà.

Alcuni esempi sono disponibili in altri post


2

Un altro scenario in cui viene visualizzato è se si utilizza il tipo di progetto "Sito Web" precedente in Visual Studio. Per quel tipo di progetto, non è in grado di fare riferimento a DLL che si trovano al di fuori della propria struttura di directory (cartella corrente e giù). Quindi nella risposta sopra, diciamo che la struttura della tua directory è simile alla seguente:

inserisci qui la descrizione dell'immagine

Dove ProjectX e ProjectY sono directory padre / figlio e ProjectX fa riferimento a A.dll che a sua volta fa riferimento a B.dll e B.dll si trova all'esterno della struttura della directory, ad esempio in un pacchetto Nuget nella radice (Pacchetti), quindi A. dll verrà inclusa, ma B.dll no.


0

Ho avuto un problema simile oggi, e questa non è certamente la risposta alla tua domanda. Ma vorrei informare tutti e possibilmente fornire una scintilla di intuizione.

Ho un'applicazione ASP.NET. Il processo di compilazione è impostato su clean e quindi build.

Ho due script Jenkins CI . Uno per la produzione e uno per la messa in scena. Ho distribuito la mia applicazione in staging e tutto ha funzionato bene. Distribuito in produzione e mancava un file DLL a cui faceva riferimento. Questo file DLL era solo nella radice del progetto. Non in alcun repository NuGet. La DLL è stata impostata su do not copy.

Lo script CI e l'applicazione erano gli stessi tra le due distribuzioni. Ancora dopo la pulizia e la distribuzione nell'ambiente di gestione temporanea, il file DLL è stato sostituito nel percorso di distribuzione dell'applicazione ASP.NET ( bin/). Questo non era il caso dell'ambiente di produzione.

Si scopre in un ramo di test che avevo aggiunto un passaggio al processo di compilazione per copiare su questo file DLL nella bindirectory. Ora la parte che ha impiegato un po 'di tempo per capire. Il processo CI non si stava pulendo da solo. La DLL è stata lasciata nella directory di lavoro ed è stata impacchettata accidentalmente con il file .zip ASP.NET. Nel ramo di produzione non è mai stato copiato il file DLL allo stesso modo e non lo ha mai distribuito accidentalmente.

TLDR; Controlla e assicurati di sapere cosa sta facendo il tuo server di build.


0

Assicurati che entrambi i progetti siano nella stessa versione .net, controlla anche la copia della proprietà locale, ma questa dovrebbe essere truel'impostazione predefinita


0

Utilizzando Visual Studio 2015 aggiungendo il parametro aggiuntivo

/ Deployonbuild = false

alla riga di comando msbuild risolto il problema.


-1

Ho appena riscontrato un problema molto simile. Durante la compilazione utilizzando Visual Studio 2010, il file DLL è stato incluso nella bincartella. Ma durante la compilazione con MSBuild il file DLL di terze parti non era incluso.

Molto frustrante. Il modo in cui l'ho risolto è stato quello di includere il riferimento NuGet al pacchetto nel mio progetto Web anche se non lo sto usando direttamente lì.


Questo è un duplicato della risposta principale.
Giles Roberts,

-3

Includere tutti i file DLL referenziati dalle tue referenze di progetto nel progetto del sito Web non è sempre una buona idea, soprattutto quando stai usando l' iniezione di dipendenza : il tuo progetto web vuole solo aggiungere un riferimento al file / progetto DLL dell'interfaccia, non a qualsiasi DLL di implementazione concreta file.

Perché se si aggiunge un riferimento direttamente a un file / progetto DLL di implementazione, non è possibile impedire allo sviluppatore di chiamare un "nuovo" su classi concrete del file / progetto DLL di implementazione anziché tramite l'interfaccia. È anche stato dichiarato un "hardcode" nel tuo sito Web per utilizzare l'implementazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.