MSBuild nie kopiuje referencji (plików DLL), jeśli używasz zależności projektu w rozwiązaniu

273

Mam cztery projekty w moim rozwiązaniu Visual Studio (wszyscy celują w .NET 3.5) - dla mojego problemu tylko te dwa są ważne:

  1. MyBaseProject <- ta biblioteka klas odwołuje się do pliku DLL innej firmy (elmah.dll)
  2. MyWebProject1 <- ten projekt aplikacji internetowej ma odniesienie do MyBaseProject

Dodałem odniesienie elmah.dll do MyBaseProject w Visual studio 2008, klikając „Dodaj odniesienie ...” → karta „Przeglądaj” → wybierając „elmah.dll”.

Właściwości odwołania Elmah są następujące:

  • Aliasy - globalne
  • Kopiuj lokalnie - prawda
  • Kultura -
  • Opis - moduły rejestrowania błędów i moduły obsługi (ELMAH) dla ASP.NET
  • Typ pliku - zespół
  • Ścieżka - D: \ webs \ otherfolder \ _myPath \ __ tools \ elmah \ Elmah.dll
  • Rozwiązane - prawda
  • Wersja środowiska wykonawczego - v2.0.50727
  • Określona wersja - fałsz
  • Silne imię - fałszywe
  • Wersja - 1.0.11211.0

W MyWebProject1 dodałem odwołanie do projektu MyBaseProject poprzez: „Dodaj odniesienie ...” → zakładka „Projekty” → wybierając „MyBaseProject”. Właściwości tego odwołania są takie same, z wyjątkiem następujących elementów:

  • Opis -
  • Ścieżka - D: \ webs \ CMS \ MyBaseProject \ bin \ Debug \ MyBaseProject.dll
  • Wersja - 1.0.0.0

Jeśli uruchomię kompilację w Visual Studio, plik elmah.dll zostanie skopiowany do katalogu bin mojego MyWebProject1 wraz z MyBaseProject.dll!

Jeśli jednak wyczyszczę i uruchomię MSBuild dla rozwiązania (przez D: \ webs \ CMS> C: \ WINDOWS \ Microsoft.NET \ Framework \ v3.5 \ MSBuild.exe / t: ReBuild / p: Configuration = Debug MyProject.sln ) elmah.dll brakuje w katalogu bin MyWebProject1 - chociaż sama kompilacja nie zawiera ostrzeżeń ani błędów!

Już upewniłem się, że .csproj MyBaseProject zawiera element prywatny o wartości „true” (powinien to być alias „ kopiuj lokalnie ” w 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>

(Tag prywatny nie pojawił się domyślnie w pliku xml .csproj, chociaż Visual Studio powiedział „kopiuj lokalnie” true. Zmieniłem „kopiuj lokalnie” na false - zapisałem - i ustawiłem ponownie z powrotem na true - zapisz!)

Co jest nie tak z MSBuild? Jak uzyskać odniesienie (elmah.dll) skopiowane do kosza MyWebProject1?

NIE chcę dodawać operacji kopiowania po zakończeniu budowy do polecenia po zakończeniu każdego projektu! (Wyobraź sobie, że wiele projektów zależy od MyBaseProject!)

toebens
źródło
11
Chciałbym uzyskać jaśniejszą odpowiedź na pytanie, dlaczego tak się dzieje.
David Faivre
1
Spójrz na udzieloną tutaj
Anuroopa Shenoy,
1
jakieś ostateczne rozwiązanie z pełną próbką kodu źródłowego?
Kiquenet
2
Zobacz odpowiedź stackoverflow.com/a/21055664/21579 poniżej autorstwa @deadlydog. Doskonałe wyjaśnienie i rozwiązałem problem dla mnie ... najczęściej głosowana odpowiedź poniżej jest nieprawidłowa dla VS2012.
Jeff Widmer

Odpowiedzi:

153

Nie jestem pewien, dlaczego różni się to podczas budowania między Visual Studio i MsBuild, ale oto, co znalazłem, gdy napotkałem ten problem w MsBuild i Visual Studio.

Wyjaśnienie

W przykładowym scenariuszu powiedzmy, że mamy projekt X, zespół A i zespół B. Zespół A odwołuje się do zespołu B, więc projekt X zawiera odniesienie zarówno do A, jak i B. Ponadto projekt X zawiera kod, który odwołuje się do zespołu A (np. A. SomeFunction ()). Teraz tworzysz nowy projekt Y, który odwołuje się do projektu X.

Tak więc łańcuch zależności wygląda następująco: Y => X => A => B

Visual Studio / MSBuild stara się być inteligentny i przenosić tylko referencje do projektu Y, które wykrywa jako wymagane przez projekt X; robi to, aby uniknąć zanieczyszczenia referencyjnego w projekcie Y. Problem polega na tym, że ponieważ projekt X tak naprawdę nie zawiera kodu, który jawnie używa zestawu B (np. B.SomeFunction ()), VS / MSBuild nie wykrywa, że ​​B jest wymagane przez X, a zatem nie kopiuje go do katalogu bin projektu Y; kopiuje tylko zespoły X i A.

Rozwiązanie

Masz dwie opcje rozwiązania tego problemu, z których każda spowoduje skopiowanie zestawu B do katalogu bin projektu Y:

  1. Dodaj odniesienie do zestawu B w projekcie Y.
  2. Dodaj fikcyjny kod do pliku w projekcie X, który używa zestawu B.

Osobiście wolę opcję 2 z kilku powodów.

  1. Jeśli w przyszłości dodasz inny projekt, który odwołuje się do projektu X, nie będziesz musiał pamiętać o dołączeniu odniesienia do zestawu B (tak jak w przypadku opcji 1).
  2. Możesz mieć wyraźne komentarze mówiące, dlaczego fałszywy kod musi tam być, a nie go usuwać. Więc jeśli ktoś usunie kod przez przypadek (powiedzmy za pomocą narzędzia refaktoryzującego, które szuka nieużywanego kodu), możesz łatwo zobaczyć z kontroli źródła, że ​​kod jest wymagany i go przywrócić. Jeśli użyjesz opcji 1, a ktoś użyje narzędzia refaktoryzującego do wyczyszczenia nieużywanych referencji, nie masz żadnych komentarzy; zobaczysz tylko, że odwołanie zostało usunięte z pliku .csproj.

Oto próbka „fałszywego kodu”, który zazwyczaj dodaje się, gdy napotykam tę sytuację.

    // 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);
    }
śmiertelny pies
źródło
4
Nie omawia się tutaj tego, że istnieje różnica między kopiowaniem produktów kompilacji w / bin projektu WWW a rutynowym kopiowaniem do docelowego katalogu wyjściowego (np. / Bin / x86 / Debug). Ten pierwszy jest wykonywany przez projekt, do którego się odwołuje, podczas kompilacji, a drugi przez zależny projekt WWW. Badanie Microsoft.Common.targets pomaga to zrozumieć. Kopie do sieci / bin wcale nie zależą od lokalnego zachowania kopiowania - skopiuj lokalne wpływy na kopię do docelowego katalogu docelowego, który nie jest częścią struktury, do której odwołuje się Cassini działający za pośrednictwem Debugowania.
user1164178,
6
możesz wyjaśnić, dlaczego działało z VS BEZ dodania tego „fałszywego kodu”, ale nie z msbuild?
toebens
3
Nawet mniej inwazyjny niż wywoływanie funkcji, można przypisać typ klasy zawartej w zestawie do zmiennej zastępczej. Type dummyType = typeof(AssemblyA.AnyClass);
Arytmomanka,
14
Rozwiązanie 2 pokazane powyżej będzie działać, chyba że masz zaznaczone ustawienie „Optymalizuj kod” w Visual Studio. W takim przypadku nadal wyklucza dll. Dodałem jeszcze jedną linię, aby zastąpić jej „optymalizację”. Console.WriteLine(dummyType.FullName);
JasonG
3
Rozwiązanie 2 nie działało dla mnie w Visual Studio 2017. Po raz pierwszy pomyślałem, że było tak, ponieważ w moim zestawie X korzystałem tylko enumz B i założyłem, że wyliczanie jest wprowadzane. Dodałem kod, aby bezpośrednio używać typu z B i to nie pomogło. Zawsze było tak również, że mój X używa typów w podklasie typu B, więc nie mogę zrozumieć, w jaki sposób kompilator uważa, że ​​B nie jest wymagany przez X i można go zignorować. To jest szaleństwo.
Xharlie,
170

Po prostu sobie z tym radzę. Przejdź do właściwości swojego odwołania i zrób to:

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

i to wszystko.

Program Visual Studio 2010 początkowo nie umieścił: <private>True</private> w znaczniku referencyjnym, a ustawienie „kopiuj lokalnie” na false powoduje utworzenie znacznika. Następnie ustawi odpowiednio wartość true i false.

Andrzej
źródło
10
To był dar niebios. Dziękuję Ci za to!
Rebecca
22
Nie działało dla mnie z MSBuild 4 / VS2012. To znaczy, byłem w stanie zaktualizować odniesienia, <Private>true</Private>ale powiedziałem, że nie miało to wpływu na MSBuild. Na koniec dodałem właśnie odniesienia NuGet do projektów niskiego poziomu.
Michael Teper
2
Nie działało dla mnie. Nadal nie kopiuje System.Net.Http.Formatting do folderu bin.
Akira Yamamoto,
4
Jest to odpowiednik Have you tried turning it off and on again?i działało!
guanome
6
Wygląda na to, że VS2015 nadal zachowuje się tak samo: ustawienie „Kopiuj lokalnie” na „Fałsz”, a następnie z powrotem na „Prawda” na odnośnych plikach .ll, działa.
przerzucenie
38

Jeśli nie używasz zestawu bezpośrednio w kodzie, program Visual Studio, próbując być pomocnym, wykrywa, że ​​nie jest używany i nie dołącza go do wyniku. Nie jestem pewien, dlaczego widzisz różne zachowania między Visual Studio i MSBuild. Możesz spróbować ustawić wynik kompilacji na diagnostyczny dla obu i porównać wyniki, aby zobaczyć, gdzie się różni.

Jeśli chodzi o odniesienie do elmah.dll, jeśli nie odwołujesz się bezpośrednio do niego w kodzie, możesz dodać go jako element do projektu i ustawić Działanie kompilacji na Contentoraz Kopiuj do katalogu wyjściowego na Always.

John Hunter
źródło
3
+1 za twoją kopię komentarza do katalogu wyjściowego, jeśli Elmah nie jest używane w kodzie, warto kopiować jako treść.
Kit Roed
4
Rzeczywiście, ignoruje on zespoły, które nie są używane, ALE jedną ważną rzeczą do zapamiętania, ponieważ od VS 2010 używające asemblera w słownikach zasobów XAML nie jest uważane za używanie przez VS bezpośrednio, więc nie będzie go kopiować.
Alex Burtsev,
Jest to lepsze w przypadku projektu testu jednostkowego, w którym potrzebuję biblioteki dll. Nie chcę dodawać biblioteki DLL, że główny projekt nie musi tylko uruchamiać testów!
Lukos
14

Spojrzeć na:

Ten wątek forum MSBuild rozpocząłem

Znajdziesz tam moje tymczasowe rozwiązanie / obejście!

(MyBaseProject potrzebuje kodu odwołującego się do niektórych klas (cokolwiek) z pliku elmah.dll, ponieważ plik elmah.dll jest kopiowany do kosza MyWebProject1!)

toebens
źródło
1
Cholera - to jedyne rozwiązanie, do którego doszedłem - mając nadzieję, że może być lepszy sposób!
nickspoon
2
Zobacz odpowiedź Andrzeja poniżej, aby uzyskać mniej hackujące rozwiązanie (bez obrazy!)
MPritchard
1
Dla tych, którzy teraz na to patrzą, odpowiedź Toebensa na MSDN jest zasadniczo taka sama, jak odpowiedź śmiertelnego psa , podana kilka lat później.
jpaugh
8

Miałem ten sam problem.

Sprawdź, czy wersja ramowa projektu jest taka sama jak wersja ramowa biblioteki dll, do której się odwołujesz.

W moim przypadku mój klient został skompilowany za pomocą „Framework 4 Client”, a DLL znajdował się w „Framework 4”.

Andre Avelar
źródło
6

Problem, z którym się spotkałem, to posiadanie projektu zależnego od projektu bibliotecznego. Aby zbudować, wykonałem następujące kroki:

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

To oczywiście oznaczało, że brakowało mi plików DLL mojej biblioteki w bin, a co najważniejsze w pakiecie zip. Uważam, że działa to idealnie:

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

Nie mam pojęcia, dlaczego to działa ani dlaczego w ogóle nie działa. Ale nadzieja to pomaga.

Vangora
źródło
Miałem ten sam problem z budowaniem całego rozwiązania za pomocą / t: Kompilacja z TeamCity w jednym kroku, a następnie w następnym / t: Pakiet w projekcie WebAPI. Wszelkie biblioteki dll, do których odwołują się jakiekolwiek odniesienia do projektu, nie zostały uwzględnione. Zostało to naprawione za pomocą powyższego - / T: Rebuild; Pakiet w interfejsie WebAPI zawierał te biblioteki dll.
Andy Hoyle,
5

Właśnie miałem ten sam problem i okazało się, że jest to spowodowane tym, że 2 projekty w tym samym rozwiązaniu odwoływały się do innej wersji biblioteki innej firmy.

Po poprawieniu wszystkich odniesień wszystko działało idealnie.

Steven Engels
źródło
4

Jak wspomniał Alex Burtsev w komentarzu, wszystko, co jest używane tylko w słowniku zasobów XAML, lub w moim przypadku, wszystko, co jest używane tylko w XAML, a nie w kodzie, nie jest uważane za „używane” przez MSBuild.

Tak więc po prostu odświeżenie fałszywego odwołania do klasy / komponentu w zestawie w jakimś kodzie było wystarczające, aby przekonać MSBuild, że zestaw faktycznie jest w użyciu.

Scott
źródło
To był dokładnie mój problem i rozwiązanie, które zadziałało. Spędziłem zbyt długo, próbując to rozgryźć. Dzięki Scott i @Alex Burstev
karol
Dobry Boże, doprowadzało mnie to do szaleństwa. Tak , korzystałem z FontAwesome.WPF i tylko z XAML (z oczywistych powodów). Pomogło dodanie sztucznej metody. Dzięki! I tak, nadal dotyczy to VS 2017 15.6, więc zgłosiłem
Sören Kuklau
3

Zmiana docelowej struktury z .NET Framework 4 Client Profile na .NET Framework 4 naprawiła dla mnie ten problem.

W twoim przykładzie: ustaw docelową platformę MyWebProject1 na .NET Framework 4

Fuyu Persimmon
źródło
3

Korzystając ze schematu Deadlydog,

Y => X => A => B ,

mój problem polegał na tym, że kiedy zbudowałem Y, zespoły (A i B, wszystkie 15 z nich) z X nie pojawiały się w folderze bin Y.

Rozwiązałem problem, usuwając odniesienie X z Y, zapisz, buduj, a następnie ponownie dodaj odniesienie X (odwołanie do projektu), a następnie zapisz, buduj, a A i B zaczęły pojawiać się w folderze bin Y.

J Z
źródło
Po wielu godzinach poszukiwań i wypróbowania wielu innych rozwiązań, to zadziałało dla mnie.
Suncat2000
2

Miałem ten sam problem i dll był dynamicznie ładowanym odwołaniem. Aby rozwiązać problem, dodałem „używanie” z przestrzenią nazw dll. Teraz dll jest kopiowany do folderu wyjściowego.

Spamuj mnie
źródło
2

Wymaga to dodania .targetspliku do projektu i ustawienia włączenia go do sekcji zawierającej projekt.

Zobacz moją odpowiedź tutaj dla procedury.

Alex Essilfie
źródło
2

Odwoływanie się do zestawów, które nie są używane podczas kompilacji, nie jest prawidłową praktyką. Powinieneś rozszerzyć swój plik kompilacji, aby skopiował dodatkowe pliki. Albo za pomocą zdarzenia po kompilacji lub poprzez aktualizację grupy właściwości.

Niektóre przykłady można znaleźć w innym poście

verbedr
źródło
2

Innym scenariuszem, w którym się to pokazuje, jest użycie starszego typu projektu „Witryna sieci Web” w programie Visual Studio. W przypadku tego typu projektu nie można odwoływać się do plików DLL, które znajdują się poza własną strukturą katalogów (bieżący folder i dół). W powyższej odpowiedzi powiedzmy, że twoja struktura katalogów wygląda następująco:

wprowadź opis zdjęcia tutaj

Gdzie ProjectX i ProjectY są katalogami nadrzędnymi / podrzędnymi, a ProjectX odwołuje się do A.dll, który z kolei odwołuje się do B.dll, a B.dll znajduje się poza strukturą katalogów, na przykład w pakiecie Nuget w katalogu głównym (Pakiety), a następnie A. biblioteka DLL zostanie dołączona, ale B.dll nie.

Focker
źródło
0

Miałem dzisiaj podobny problem i to z pewnością nie jest odpowiedź na twoje pytanie. Ale chciałbym poinformować wszystkich i być może dostarczyć iskry wglądu.

Mam aplikację ASP.NET. Proces kompilacji jest ustawiony na czyszczenie, a następnie kompilację.

Mam dwa skrypty Jenkins CI . Jeden do produkcji, a drugi do inscenizacji. Wdrożyłem moją aplikację na platformie i wszystko działało dobrze. Wdrożono do produkcji i brakowało pliku DLL, do którego się odwołano. Ten plik DLL znajdował się w katalogu głównym projektu. Nie ma w żadnym repozytorium NuGet. Biblioteka DLL została ustawiona nado not copy .

Skrypt CI i aplikacja były takie same między dwoma wdrożeniami. Nadal po wyczyszczeniu i wdrożeniu w środowisku pomostowym plik DLL został zastąpiony w miejscu wdrożenia aplikacji ASP.NET ( bin/). Nie było tak w przypadku środowiska produkcyjnego.

Okazuje się, że w gałęzi testowej dodałem krok do procesu kompilacji, aby skopiować ten plik DLL do binkatalogu. Teraz część, która zajęła trochę czasu, aby się zorientować. Proces CI nie czyścił się sam. Biblioteka DLL została pozostawiona w katalogu roboczym i została przypadkowo spakowana z plikiem .zip ASP.NET. Oddział produkcyjny nigdy nie kopiował pliku DLL w ten sam sposób i nigdy nie wdrożył go przypadkowo.

TLDR; Sprawdź i upewnij się, że wiesz, co robi Twój serwer kompilacji.

Jason Portnoy
źródło
0

Upewnij się, że oba projekty są w tej samej wersji .net, sprawdź także kopiuj lokalną właściwość, ale powinno to być trueustawienie domyślne

john.kernel
źródło
0

Za pomocą programu Visual Studio 2015 dodając dodatkowy parametr

/ deployonbuild = false

w wierszu polecenia msbuild naprawiono problem.

Giles Roberts
źródło
-1

Właśnie natrafiłem na bardzo podobny problem. Podczas kompilacji przy użyciu programu Visual Studio 2010 plik DLL został dołączony do binfolderu. Ale podczas kompilacji przy użyciu MSBuild plik DLL innej firmy nie został uwzględniony.

Bardzo frustrujące. Sposób, w jaki to rozwiązałem, polegał na dołączeniu odwołania do pakietu NuGet do mojego projektu internetowego, mimo że nie używam go bezpośrednio tam.

Gary Brunton
źródło
To jest duplikat najwyższej odpowiedzi.
Giles Roberts
-3

Uwzględnienie wszystkich plików DLL z referencji projektu w projekcie witryny nie zawsze jest dobrym pomysłem, szczególnie gdy używasz wstrzykiwania zależności : twój projekt sieciowy chce po prostu dodać odniesienie do pliku / projektu biblioteki DLL, a nie żadnej konkretnej biblioteki DLL implementacji plik.

Ponieważ jeśli dodasz odwołanie bezpośrednio do pliku / projektu DLL implementacji, nie możesz zapobiec wywoływaniu przez programistę „nowej” konkretnej klasy pliku / projektu DLL implementacji zamiast przez interfejs. Podałeś również „hardcode” na swojej stronie, aby skorzystać z implementacji.

Hung Nguyen
źródło