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 \ otherfolder \ _myPath \ __ tools \ elmah \ Elmah.dll
  • Решено - Верно
  • Runtime версия - v2.0.50727
  • Указанная версия - false
  • Сильное имя - ложь
  • Версия - 1.0.11211.0

В MyWebProject1 я добавил ссылку на проект MyBaseProject: «Добавить ссылку ...» → вкладка «Проекты» → выбрав «MyBaseProject». Свойства этой ссылки одинаковы, за исключением следующих элементов:

  • Описание -
  • Путь - D: \ webs \ CMS \ MyBaseProject \ bin \ Debug \ MyBaseProject.dll
  • Версия - 1.0.0.0

Если я запускаю сборку в Visual Studio, файл elmah.dll копируется в каталог bin моего 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 отсутствует в каталоге bin 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 сказал «копировать локальный» в значение true. Я переключил «копировать локальный» в значение «ложь» - сохранено - и снова установило значение «истина» - сохранить!)

Что не так с MSBuild? Как получить ссылку (elmah.dll), скопированную в корзину MyWebProject1?

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

toebens
источник
11
Я хотел бы получить более четкий ответ, почему это происходит.
Дэвид Фэйвр
1
Взгляните на ответ, приведенный здесь
Anuroopa Shenoy
1
какое-нибудь окончательное решение с полным примером исходного кода работает над этим?
Kiquenet
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. SomeFunction ()). Теперь вы создаете новый проект Y, который ссылается на проект X.

Таким образом, цепочка зависимостей выглядит следующим образом: Y => X => A => B

Visual Studio / MSBuild старается быть умным и привносить в проект Y только те ссылки, которые он обнаруживает в соответствии с требованиями проекта X; он делает это, чтобы избежать загрязнения ссылок в проекте Y. Проблема в том, что проект X на самом деле не содержит никакого кода, который явно использует сборку B (например, B.SomeFunction ()), VS / MSBuild не обнаруживает, что требуется B X и, следовательно, не копирует его в каталог bin проекта Y; он копирует только сборки X и A.

Решение

У вас есть два варианта решения этой проблемы, каждый из которых приведет к копированию сборки B в каталог bin проекта 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);
    }
deadlydog
источник
4
Здесь не обсуждается то, что существует разница между копированием продуктов сборки в / bin веб-проекта и обычным копированием в целевой выходной каталог (например, / bin / x86 / Debug). Первый выполняется ссылочным проектом при сборке, а второй - зависимым веб-проектом. Изучение Microsoft.Common.targets помогает понять это. Копии в web / bin вообще не зависят от локального поведения копирования - копируйте локальные воздействия на копию в выходной целевой каталог, который не является частью структуры, на которую ссылается Cassini, работающий через Debug.
user1164178
6
Вы можете объяснить, почему он работал с VS БЕЗ добавления этого "фиктивного кода", но не с msbuild?
Toebens
3
Даже менее инвазивный, чем вызов функции, вы можете назначить тип класса, содержащийся внутри сборки, фиктивной переменной. Type dummyType = typeof(AssemblyA.AnyClass);
Arithmomaniac
14
Решение № 2, как показано выше, будет работать, если в Visual Studio не установлен флажок «Оптимизировать код». В этом случае он все равно будет исключать DLL. Я добавил еще одну строку, чтобы отменить ее «оптимизацию». Console.WriteLine(dummyType.FullName);
JasonG
3
Решение 2 не работало для меня в Visual Studio 2017. Сначала я подумал, что это потому, что в моей сборке X я использовал только оператор enumfrom B и предположил, что enum вставляется. Я добавил код, чтобы напрямую использовать тип из B, и это не помогло. Также всегда было так, что my X использует типы в A, которые подклассируют типы в B, поэтому я не могу понять, как компилятор считает, что B не требуется X и может быть проигнорировано. Это помешанные.
Ксарли
170

Я просто так с этим справляюсь. Перейдите в свойства вашей ссылки и сделайте это:

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

и это все.

Visual Studio 2010 изначально не помещается: <private>True</private> в тег ссылки и установка «копировать локальное» в значение false приводит к созданию тега. После этого он установит его в true и false соответственно.

Андрей
источник
10
Это была находка. Спасибо тебе за это!
Ребекка
22
У меня не работало с MSBuild 4 / VS2012. То есть я смог обновить ссылки, чтобы сказать, <Private>true</Private>но это, похоже, не влияло на MSBuild. В конце я просто добавил ссылки NuGet на проекты нижнего уровня.
Майкл Тепер
2
Не работал для меня Он по-прежнему не копирует System.Net.Http.Formatting в папку bin.
Акира Ямамото
4
Это эквивалентно Have you tried turning it off and on again?, и это сработало!
Гуаном
6
Похоже, что VS2015 по-прежнему ведет себя так же: установка «Локальное копирование» на «Ложь» и затем «Истина» для указанных DLL-файлов работает.
флип
38

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

Что касается ссылки на elmah.dll, если вы не ссылаетесь на нее напрямую в коде, вы можете добавить ее в качестве элемента в свой проект и установить для параметра «Действие сборки» значение Content«Копировать в выходной каталог» Always.

Джон Хантер
источник
3
+1 для вашей копии в комментарии к выходному каталогу, если Elmah не используется в коде, имеет смысл копировать как контент.
Кит Роуд
4
Действительно, он игнорирует сборки, которые не используются, НО одну важную вещь, чтобы отметить, что в VS 2010 использование сборок в словарях ресурсов XAML не рассматривается как использование ассемблера VS, поэтому он не будет копировать его.
Алексей Бурцев
Это лучше для проекта модульного тестирования, где мне нужна DLL. Я не хочу добавлять DLL, которая не нужна основному проекту только для запуска тестов!
Лукос
14

Взгляни на:

Эту ветку форума MSBuild я начал

Вы найдете мое временное решение / обходной путь там!

(MyBaseProject требуется некоторый код, который ссылается на некоторые классы (что угодно) из elmah.dll для elmah.dll, копируемой в корзину MyWebProject1!)

toebens
источник
1
Черт возьми - это единственное решение, к которому я тоже пришел - надеялся, что может быть лучший способ сделать это!
Никпун
2
Смотрите ответ Эндрю ниже для менее хакерского решения (без обид!)
MPritchard
1
Для тех, кто смотрит на это сейчас, ответ toebens на MSDN по сути такой же, как и ответ Deadlydog , предоставленный спустя несколько лет.
18:30
8

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

Проверьте, совпадает ли базовая версия вашего проекта с базовой версией библиотеки DLL, на которую вы ссылаетесь.

В моем случае мой клиент был скомпилирован с использованием «Framework 4 Client», а DLL была в «Framework 4».

Андре Авелар
источник
6

Проблема, с которой я столкнулся, заключалась в том, что у меня есть проект, который зависит от проекта библиотеки. Чтобы построить, я следовал за этими шагами:

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

Это, конечно, означало, что мне не хватало dll-файлов моей библиотеки в bin и, самое главное, в zip-файле пакета. Я нашел это работает отлично:

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

Я понятия не имею, почему эта работа или почему это не было в первую очередь. Но надеюсь, что это помогает.

vangorra
источник
У меня была та же проблема, которая заключалась в построении целого решения с использованием / t: Build из TeamCity за один шаг, а затем в следующем / t: Package on WebAPI проекте. Все библиотеки, на которые ссылаются любые ссылки на проекты, не были включены. Это было исправлено с помощью вышеупомянутого - / T: Rebuild; пакет в WebAPI затем включал эти библиотеки.
Энди Хойл
5

У меня была точно такая же проблема, и это, как оказалось, было вызвано тем фактом, что 2 проекта в одном решении ссылались на другую версию сторонней библиотеки.

Как только я исправил все ссылки, все работало отлично.

Стивен Энгельс
источник
4

Как отметил в своем комментарии Алекс Бурцев, все, что используется только в словаре ресурсов XAML, или, в моем случае, все, что используется только в XAML, а не в коде, не считается «используемым» MSBuild.

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

Скотт
источник
Это была именно моя проблема и решение, которое сработало. Потратил слишком много времени, пытаясь понять это. Спасибо Скотт & @
Алекс
Боже мой, это сводило меня с ума. Да , я использовал FontAwesome.WPF и только изнутри XAML (по понятным причинам). Добавление фиктивного метода помогло. Спасибо! И да, VS 2017 все еще подвержен влиянию 15.6, поэтому я подал ошибку: github.com/dotnet/roslyn/issues/25349
Sören Kuklau
3

Изменение целевой платформы с .NET Framework 4 Client Profile на .NET Framework 4 устранило эту проблему для меня.

Итак, в вашем примере: установите целевую платформу на MyWebProject1 на .NET Framework 4

Фую Хурма
источник
3

Используя схему deadlydog,

Y => X => A => B ,

моя проблема заключалась в том, что когда я собирал Y, сборки (A и B, все 15 из них) из X не показывались в папке bin Y.

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

JZ
источник
После многих часов поиска и опробования многих других решений это сработало для меня.
Suncat2000
2

У меня была такая же проблема, и dll была динамически загруженной ссылкой. Для решения проблемы я добавил «использование» с пространством имен DLL. Теперь dll копируется в выходную папку.

Spamme
источник
2

Это требует добавления .targets файл в проект и настроить его для включения в раздел включений проекта.

Смотрите мой ответ здесь для процедуры.

Алекс Эссильфи
источник
2

Ссылка на сборки, которые не используются во время сборки, не является правильной практикой. Вы должны увеличить свой файл сборки, чтобы он скопировал дополнительные файлы. Либо с помощью события после сборки, либо путем обновления группы свойств.

Некоторые примеры можно найти в другом посте

verbedr
источник
2

Другой сценарий, в котором это проявляется, - это если вы используете более старый тип проекта «Веб-сайт» в Visual Studio. Для этого типа проекта он не может ссылаться на .dll, которые находятся за пределами его собственной структуры каталогов (текущей папки и вниз). Итак, в ответе выше, скажем, ваша структура каталогов выглядит так:

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

Где ProjectX и ProjectY являются родительскими / дочерними каталогами, а ProjectX ссылается на A.dll, которая, в свою очередь, ссылается на B.dll, а B.dll находится за пределами структуры каталогов, например, в пакете Nuget в корне (Packages), затем A. DLL будет включен, но B.dll не будет.

Focker
источник
0

У меня была похожая проблема сегодня, и это, безусловно, не ответ на ваш вопрос. Но я хотел бы проинформировать всех и, возможно, дать искру понимания.

У меня есть приложение ASP.NET. Процесс сборки настроен на очистку, а затем сборку.

У меня есть два сценария Jenkins CI . Один для производства и один для постановки. Я развернул свое приложение для постановки, и все работало нормально. Развернут в производство и отсутствует файл DLL, на который есть ссылка. Этот файл DLL был просто в корне проекта. Не в любом хранилище NuGet. DLL была установлена ​​наdo not copy .

Скрипт CI и приложение были одинаковыми между двумя развертываниями. После очистки и развертывания в промежуточной среде файл DLL был заменен в месте развертывания приложения ASP.NET (bin/ ). Это не относится к производственной среде.

Оказывается, в ветке тестирования я добавил шаг в процесс сборки, чтобы скопировать этот файл DLL в bin каталог. Теперь часть, которая заняла некоторое время, чтобы выяснить. Процесс CI не очищался сам. DLL была оставлена ​​в рабочем каталоге и была случайно упакована с файлом .zip ASP.NET. Производственная ветка никогда не копировала файл DLL таким же образом и никогда не развертывала это случайно.

TLDR; Проверьте и убедитесь, что вы знаете, что делает ваш сервер сборки.

Джейсон Портной
источник
0

Убедитесь, что оба проекта находятся в одной и той же версии .net, также проверьте копию локального свойства, но это должно быть trueпо умолчанию

john.kernel
источник
0

Используя Visual Studio 2015 добавление дополнительного параметра

/ Deployonbuild = ложь

в командной строке msbuild исправлена ​​проблема.

Джайлс Робертс
источник
-1

Я просто столкнулся с очень похожей проблемой. При компиляции с использованием Visual Studio 2010 файл DLL был включен в binпапку. Но при компиляции с использованием MSBuild сторонний DLL-файл не был включен.

Очень расстраивает. Я решил это путем включения ссылки NuGet на пакет в мой веб-проект, хотя я не использую его непосредственно там.

Гари Брунтон
источник
Это дубликат верхнего ответа.
Джайлс Робертс
-3

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

Потому что, если вы добавите ссылку непосредственно к файлу / проекту реализации DLL, вы не сможете помешать вашему разработчику вызывать «новый» для конкретных классов файла / проекта реализации DLL, а не через интерфейс. Также вы указали «жесткий код» на своем веб-сайте, чтобы использовать реализацию.

Хунг Нгуен
источник