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.