MSBuild не копіює посилання (DLL-файли), якщо в проекті використовуються залежності проекту


273

У моєму рішенні Visual Studio є чотири проекти (усі націлені на .NET 3.5) - для моєї проблеми важливі лише ці два:

  1. MyBaseProject <- бібліотека цього класу посилається на сторонній файл DLL (elmah.dll)
  2. MyWebProject1 <- цей проект веб-додатків має посилання на MyBaseProject

Я додав посилання elmah.dll на MyBaseProject у Visual studio 2008, натиснувши "Додати довідку ..." → "Огляд" → виберіть "elmah.dll".

Властивості посилання Elmah такі:

  • Псевдоніми - глобальні
  • Копіювати локальне - правда
  • Культура -
  • Опис - Модулі та оброблювачі помилок (ELMAH) для ASP.NET
  • Тип файлу - Збірка
  • Шлях - D: \ webs \ other Folder \ _myPath \ __ інструменти \ elmah \ Elmah.dll
  • Вирішено - Правда
  • Версія виконання - v2.0.50727
  • Вказана версія - помилкова
  • Сильне ім'я - хибне
  • Версія - 1.0.11211.0

У MyWebProject1 я додав посилання на Project MyBaseProject шляхом: "Додати довідку ..." → "Вкладки проектів" → вибору "MyBaseProject". Властивості цієї посилання однакові, за винятком наступних членів:

  • Опис -
  • Шлях - D: \ webs \ CMS \ MyBaseProject \ bin \ Debug \ MyBaseProject.dll
  • Версія - 1.0.0.0

Якщо я запускаю збірку у Visual Studio, файл elmah.dll копіюється у бін каталог MyWebProject1 разом із MyBaseProject.dll!

Однак якщо я очищую та запускаю MSBuild для рішення (через D: \ webs \ CMS> C: \ WINDOWS \ Microsoft.NET \ Framework \ v3.5 \ MSBuild.exe / t: ReBuild / p: Configuration = Debug MyProject.sln ) elmah.dll відсутній у каталозі бін MyWebProject1 - хоча сама збірка не містить попереджень та помилок!

Я вже переконався, що .csproj MyBaseProject містить приватний елемент зі значенням "true" (це має бути псевдонім для " copy local " у 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>

(За замовчуванням приватний тег не з’явився у xml .csproj, хоча Visual Studio сказав "копіювати локальне" істинно. Я переключив "копіювати локальний" на помилковий - збережений - і знову повернув його до істинного - збережіть!)

Що не так з MSBuild? Як мені отримати посилання (elmah.dll), скопійоване у кошик MyWebProject1?

Я НЕ хочу додавати післякопіювальну копію дії до команди післязбудови кожного проекту! (Уявіть, у мене було б багато проектів залежних від MyBaseProject!)


11
Я хотів би отримати більш чітку відповідь, чому це відбувається.
Девід Файвр

1
Подивіться на відповідь, надану тут
Anuroopa Shenoy

1
будь-яке остаточне рішення з повним зразком вихідного коду, що працює над цим?
Кікенет

2
Дивіться відповідь stackoverflow.com/a/21055664/21579 нижче по @deadlydog. Відмінне пояснення і вирішило для мене питання ... відповідь, що найбільше голосує нижче, є невірною для VS2012.
Джефф Відмер

Відповіді:


153

Я не впевнений, чому це інше, коли будувати між Visual Studio та MsBuild, але ось що я виявив, коли стикався з цією проблемою в MsBuild та Visual Studio.

Пояснення

Для прикладного сценарію скажімо, що у нас є проект X, збірка A і збірка B. Складання A посилання на збірку B, тому проект X включає посилання на A і B. Також проект X включає код, на який посилається збірка A (наприклад, A. ДеякіФункції ()). Тепер ви створюєте новий проект Y, на який посилається проект X.

Отже ланцюг залежності виглядає приблизно так: Y => X => A => B

Visual Studio / MSBuild намагається бути розумним і вносити в проект Y лише посилання, які, як він вважає, вимагає проект X; це робиться для уникнення еталонного забруднення в проекті Y. Проблема полягає в тому, що проект X насправді не містить коду, який явно використовує збірку B (наприклад, B.SomeFunction ()), VS / MSBuild не виявляє, що B потрібен від X, і, отже, не копіює його у каталог бін проекту Y; він лише копіює збірки X та A.

Рішення

У вас є два варіанти вирішення цієї проблеми, обидва з яких призведуть до того, що збірка B буде скопійована до каталогу бін проекту Y:

  1. Додайте посилання на збірку B у проект Y.
  2. Додайте фіктивний код у файл у проекті X, який використовує збірку B.

Особисто я віддаю перевагу варіант 2 з кількох причин.

  1. Якщо в майбутньому ви додасте ще один проект, на який посилається проект X, вам не доведеться пам’ятати, що він також повинен містити посилання на збірку B (як, наприклад, у варіанті 1).
  2. Ви можете мати чіткі коментарі, в яких говориться, чому макетний код повинен бути там, а не видаляти його. Отже, якщо хтось видаляє код випадково (скажімо, за допомогою інструмента рефактора, який шукає невикористаний код), ви можете легко побачити з джерела управління, що код необхідний, і відновити його. Якщо ви використовуєте варіант 1, а хтось використовує інструмент рефактора для очищення невикористаних посилань, у вас немає коментарів; ви просто побачите, що з файлу .csproj було видалено посилання.

Ось зразок "макетного коду", який я зазвичай додаю, коли стикаюся з цією ситуацією.

    // 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
Що тут не обговорюється, це те, що є різниця між копіюванням збірних продуктів у / bin веб-проекту та звичайним копіюванням у цільовий вихідний каталог (наприклад, / bin / x86 / Debug). Перший робиться посиланим проектом, коли він будується, а другий виконується залежним веб-проектом. Вивчення Microsoft.Common.targets допомагає зрозуміти це. Копії в Інтернет / бін взагалі не залежать від локальної поведінки при копіюванні - скопіюйте локальний вплив на копію у вихідний цільовий dir, який не є частиною структури, на яку посилається Cassini, що працює через налагодження.
користувач1164178

6
Ви можете пояснити, чому він працював з VS БЕЗ додавання цього "фіктивного коду", але не з msbuild?
toebens

3
Навіть менш інвазивним, ніж виклик функції, ви можете присвоїти типу класу, що міститься всередині збірки, фіктивну змінну. Type dummyType = typeof(AssemblyA.AnyClass);
Арітмоман

14
Рішення №2, як показано вище, буде працювати, якщо у вас не встановлено параметр "Оптимізувати код" у Visual Studio. У такому випадку вона все одно виключатиме dll. Я додав ще один рядок, щоб перекрити його "оптимізацію". Console.WriteLine(dummyType.FullName);
JasonG

3
Рішення 2 не спрацювало для мене у Visual Studio 2017. Я спершу подумав, що це тому, що в моєму зібранні X я використовував лише enumB і вважав, що перерахунок підкреслюється. Я додав код, щоб безпосередньо використовувати тип від B, і це не допомогло. Так само завжди було так, що мій X використовує типи в A, які підкласи містять у B, тому я не можу зрозуміти, як компілятор вважає, що B не вимагає X, і його можна ігнорувати. Це бонкери.
Xharlie

170

Я просто так маю справу з цим. Перейдіть до властивостей вашої довідки і зробіть це:

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

і це все.

Visual Studio 2010 спочатку не ставиться: <private>True</private> у довідковому тезі та встановлення "копіювати локальне" на помилкове викликає його створення тегу. Після цього він встановить його відповідно до істинного та помилкового.


10
Це була знахідка. Дякую за це!
Ребекка

22
Не працював для мене з MSBuild 4 / VS2012. Тобто, мені вдалося оновити посилання, щоб сказати, <Private>true</Private>але, здавалося, це не впливає на MSBuild. Врешті-решт, я просто додав посилання NuGet до проектів нижчого рівня.
Майкл Тепер

2
Не працювало для мене. Він досі не копіює System.Net.Http.Форматирование у папку bin.
Акіра Ямамото

4
Це еквівалент Have you tried turning it off and on again?, і це спрацювало!
guanome

6
Схоже, VS2015 все ще поводиться так само: встановлення "Copy Local" на "False", потім повернення до "True" у посиланні .dll, працює.
фліп

38

Якщо ви не використовуєте збірку безпосередньо в коді, то Visual Studio, намагаючись бути корисним, виявляє, що вона не використовується, і не включає її у висновок. Я не впевнений, чому ви бачите різну поведінку між Visual Studio та MSBuild. Ви можете спробувати встановити вихід збірки на діагностичний для обох і порівняти результати, щоб побачити, куди він розходиться.

Що стосується вашої посилання elmah.dll, якщо ви не посилаєтесь на неї безпосередньо у коді, ви можете додати її як елемент до свого проекту та встановити дію збірки Contentта довідник копіювати на вихід Always.


3
+1 для вашої копії до коментаря Output Directory, якщо Elmah не використовується в коді, має сенс копіювати як вміст.
Kit Roed

4
Дійсно, він ігнорує збірки, які не використовуються, Але одне головне, що слід зазначити, оскільки VS 2010, що використовує збірку в словниках ресурсів XAML, не вважається використаним, можливо, VS, тому він не скопіює його.
Олексій Бурцев


Це краще для проекту Unit Test, де мені потрібен dll. Я не хочу додавати DLL, що основний проект не потребує просто тестування!
Лукос

14

Подивись на:

Цю тему на форумі MSBuild я почав

Ви знайдете моє тимчасове рішення / вирішення там!

(MyBaseProject потребує деякого коду, який посилається на деякі класи (що завгодно) з elmah.dll, щоб файл elmah.dll був скопійований у бін MyWebProject1!)


1
Чорт - це єдине рішення, до якого я теж прийшов - сподівався, що може бути кращий спосіб це зробити!
nickspoon

2
Дивіться відповідь Андреу нижче щодо менш хакітного рішення (без образи!)
MPritchard

1
Для тих, хто зараз дивиться на це, відповідь ніг на MSDN - це по суті те саме , що відповідь смертельної собаки , надана через кілька років.
jpaugh

8

У мене була така ж проблема.

Перевірте, чи версія рамки вашого проекту збігається з рамковою версією DLL, яку ви посилаєте на посилання.

У моєму випадку мій клієнт був складений за допомогою "Framework 4 Client", а DLL - у "Framework 4".


6

Питання, з яким я стикався, це те, що у мене є проект, який залежить від бібліотечного проекту. Для того, щоб будувати, я виконував наступні кроки:

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

Це, звичайно, означало, що мені не вистачає файлів DLL моєї бібліотеки у біні та, головне, у zip-файлі пакету. Я виявив, що це працює чудово:

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

Я поняття не маю, чому ця робота чи чому вона не відбулася в першу чергу. Але надія, яка допомагає.


У мене було те саме питання створення цілого рішення за допомогою / t: Build з TeamCity за один крок, а потім для наступного / t: Пакет для проекту WebAPI. Будь-які dll, на які посилаються будь-які посилання на проект, не включалися. Це було виправлено за допомогою вищезгаданого - / T: Rebuild; Пакет у WebAPI потім включав ці dll.
Енді Хойл

5

У мене просто була та сама проблема, і вона виявилася причиною того, що два проекти в одному і тому ж рішенні посилалися на іншу версію сторонньої бібліотеки.

Одного разу я виправив усі посилання, все працювало чудово.


4

Як зазначив у коментарі Алекс Бурцев, все, що використовується лише у словнику ресурсів XAML, або в моєму випадку, все, що використовується тільки в XAML, а не в коді позаду, MSBuild не вважається "використаним".

Тож просто нове створення фіктивного посилання на клас / компонент у складі в деякому коді позаду було достатньо переконати MSBuild у тому, що збірка фактично використовується.


Це було саме моє питання та рішення, яке спрацювало. Провели занадто довго, намагаючись зрозуміти це. Завдяки Скотт і @Alex Burstev
Кароль

Добре горе, це зводило мене з розуму. Так , я використовував FontAwesome.WPF, і тільки зсередини XAML (з очевидних причин). Додавання фіктивного методу допомогло. Дякую! І так, VS 2017 15.6 все ще постраждав, тому я подав помилку: github.com/dotnet/roslyn/isissue/25349
Sören Kuklau

3

Зміна цільової рамки з .NET Framework 4 профілю клієнта на .NET Framework 4 вирішила цю проблему для мене.

Отже, у вашому прикладі: встановіть цільову рамку на MyWebProject1 на .NET Framework 4


3

Використовуючи схему смертельної собаки,

Y => X => A => B ,

моя проблема полягала в тому, що коли я створив Y, збірки (A і B, всі 15) з X не відображалися в папці Y в кошику.

Я вирішив це, видаливши посилання X з Y, збережіть, складіть, потім повторно додайте посилання X (посилання на проект), і збережіть, складіть, і A і B почали відображатися у папці бін Y.


Після багатьох годин пошуку та спробу багатьох інших рішень, це працювало для мене.
Suncat2000

2

У мене була така ж проблема, і dll був динамічно завантаженим посиланням. Для вирішення проблеми я додав "використання" з простором імен DLL. Тепер dll копіюється у папку виводу.



2

Посилання на збірки, які не використовуються під час збирання, не є правильною практикою. Слід збільшити файл складання, щоб він скопіював додаткові файли. Або за допомогою події побудови повідомлення або оновлення групи властивостей.

Деякі приклади можна знайти в іншій публікації


2

Інший сценарій, коли це відображається, це якщо ви використовуєте старіший тип проекту "Веб-сайт" у Visual Studio. Для цього типу проекту він не може посилатись на .dlls, які знаходяться поза власною структурою каталогу (поточна папка та вниз). Отже, у відповіді вище, скажімо, структура вашого каталогу виглядає так:

введіть тут опис зображення

Якщо ProjectX і ProjectY - це каталоги батьків / дочір, а ProjectX посилається на A.dll, який, у свою чергу, посилається на B.dll і B.dll, знаходиться поза структурою каталогу, наприклад, у пакунку Nuget у корені (Packages), тоді A. dll буде включено, але B.dll не буде.


0

У мене було подібне питання сьогодні, і це, звичайно, не відповідь на ваше запитання. Але я хотів би повідомити всіх і, можливо, надати іскру розуміння.

У мене є програма ASP.NET. Процес збирання встановлюється на очищення, а потім на збирання.

У мене є два сценарії Джінкінс CI . Один для виробництва та один для постановки. Я розгорнув свою програму для постановки, і все працювало чудово. Розгорнуто на виробництво і не було файлу DLL, на який було посилатися. Цей файл DLL був просто в корені проекту. Ні в жодному сховищі NuGet. DLL було встановлено на do not copy.

Сценарій CI та програма були однакові між двома розгортаннями. Ще після очищення та розгортання в середовищі постановки файл DLL було замінено на місце розгортання програми ASP.NET ( bin/). Це не стосувалося виробничого середовища.

Виявляється, у галузі тестування я додав крок до процесу збирання, щоб скопіювати цей файл DLL у binкаталог. Тепер частина, яка зайняла трохи часу, щоб розібратися. Процес ІП не був очищенням сам по собі. DLL була залишена в робочому каталозі і випадково упакована у файл .zip з файлом ASP.NET. У виробничій галузі ніколи не було скопійовано файл DLL таким же чином, і його ніколи не випадково розгортали.

TLDR; Перевірте і переконайтеся, що ви знаєте, що робить ваш сервер збирання.


0

Переконайтеся, що обидва проекти у одній версії .net також перевіряють місцеве власність копіювання, але це має бути trueза замовчуванням


0

Використання Visual Studio 2015 додавання додаткового параметра

/ Depllonbuild = false

до командного рядка msbuild виправлена ​​проблема.


-1

Я просто натрапив на дуже схоже питання. При компілюванні за допомогою Visual Studio 2010 файл DLL був включений у binпапку. Але при компілюванні за допомогою MSBuild сторонній файл DLL не включався.

Дуже засмучує. Я вирішив це - включити в свій веб-проект посилання NuGet, хоча я не використовую його безпосередньо там.


Це дублікат верхньої відповіді.
Giles Roberts

-3

Включення всіх посилаються на файли DLL з ваших проектних налаштувань у проект Веб-сайт не завжди є гарною ідеєю, особливо коли ви використовуєте ін'єкцію залежностей : ваш веб-проект просто хоче додати посилання на файл / проект DLL інтерфейсу, а не будь-яку конкретну реалізацію DLL файл.

Оскільки, якщо ви додасте посилання безпосередньо на файл / проект DLL реалізації, ви не можете перешкодити розробнику викликати "нове" на конкретних класах реалізації DLL-файла / проекту, а не через інтерфейс. Крім того, ви вказали "жорсткий код" на своєму веб-сайті для використання цієї програми.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.