Как заставить проекты .NET Core копировать ссылки NuGet в выходные данные сборки?

112

Я пытаюсь написать систему подключаемых модулей с .NET Core, и одно из моих требований состоит в том, чтобы иметь возможность распространять библиотеку подключаемого модуля вместе с его зависимостями для установки пользователю.

Однако я не могу понять, как включить мои зависимости NuGet в качестве артефакта сборки и вывести их в папку сборки без необходимости использовать их dotnet publishв качестве взлома. Есть ли способ указать это в файле .csproj (файле проекта)?

Чыыран
источник
2
Зачем использовать dotnet publishхак? Включите команду в ваш файл csproj как сценарий пост-сборки.
Остин Дренски
3
dotnet publishпомещает весь фреймворк в папку публикации, поскольку я пишу плагин, большинство файлов не нужны, поскольку фреймворк уже был бы загружен программой загрузчика. Я ищу что-то похожее на то, как сборки работают в .NET Framework.
чыыран
И включение <CopyToOutputDirectory>Always</CopyToOutputDirectory>в ваш csproj каждой из DLL, которые вы хотите переместить, не помогает? Возможно совмещать с <link>узлом?
Остин Дренски
7
<PackageReference/>не поддерживает <CopyToOutputDirectory>.
чыыран 08
1
«Вся структура» исходит от NuGet ... и если вы выберете копирование всех сборок NuGet в выходные данные сборки, вы получите их все ..
Мартин Ульрих,

Ответы:

180

Вы можете добавить это в <PropertyGroup>свой файл csproj, чтобы принудительно копировать сборки NuGet в выходные данные сборки:

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

Однако обратите внимание, что вывод сборки ( bin/Release/netcoreapp*/*) не должен быть переносимым и распространяемым, вывод dotnet publish- это. Но в вашем случае копирование сборок в выходные данные сборки, вероятно, очень полезно для целей тестирования. Но обратите внимание, что вы также можете использовать DependencyContextapi для разрешения библиотек DLL и их местоположений, которые являются частью графа зависимостей приложения, вместо перечисления локального каталога.

Мартин Ульрих
источник
7
Это заставляет копировать все библиотеки DLL, а не только библиотеки Nuget,
Мохаммад Дайян,
2
Ядро 2 Я тоже получаю все библиотеки Microsoft DLL. Не знаю, почему, но раньше я получал только NuGet, но потом он перестал это делать? раздражает
Петр Кула
4
@MartinUllrich Не могли бы вы рассказать об этом DependencyContext? Как я могу использовать его для поиска библиотеки DLL, которой нет в каталоге приложения? А где это вообще?
ygoe
2
не работает для меня ядро ​​asp.net не копирует System.ValueTuple.dll
Али Юсефи
1
@AliYousefie system.valuietuple для проектов фреймворка .net больше не должен поступать из NuGet в последних версиях фреймворка .net и инструментах сборки
Мартин Ульрих
10

Вы можете использовать PostBuildEvent для автоматизации развертывания модуля при сборке.

Чтобы получить сборки NuGet в папке сборки, добавьте csproj вашего модуля

<PropertyGroup>
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>

Определите, какие файлы модулей вы хотите использовать, используя Include / Exclude (при необходимости измените путь)

<ItemGroup>
    <ModuleFiles
      Include="$(TargetDir)*.dll"
      Exclude="$(TargetDir)System*.dll;$(TargetDir)Microsoft*.dll"
      DestinationPath="$(SolutionDir)src\MyProject\Modules\MyModule\%(Filename)%(Extension)">
    </ModuleFiles>
</ItemGroup>

Сбросьте папку сборки по умолчанию и добавьте PostbuildEvent

<Target Name="PublishModule" AfterTargets="PostBuildEvent" Inputs="@(ModuleFiles)" Outputs="@(ModuleFiles->'%(DestinationPath)')">
    <WriteLinesToFile File="$(SolutionDir)src\[YOURAPP]\app_offline.htm" />
    <Copy SourceFiles="@(ModuleFiles)" DestinationFiles="@(ModuleFiles->'%(DestinationPath)')" />
    <Delete Files="$(SolutionDir)src\[YOURAPP]\app_offline.htm" />
</Target>

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

Ксивис
источник
В моем проекте у меня есть зависимость от библиотеки Nuget «Microsoft.Extensions.Logging.Log4Net.AspNetCore», и она не является частью NetCore, поэтому этот подход не сработает
sad_robot
4

Добавление

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

не сработало, но добавив это в файл Framework .csproj:

<RestoreProjectStyle>PackageReference</RestoreProjectStyle>

сделал.

Майк Бруннер
источник
Это сработало для меня, когда я ссылался на библиотеки .net Standard 2.0 из проекта .Net Framework 4.7.2. Больше ничего не исправляло.
Grungondola
3

Я «решил» (создал обходной путь) это более простым способом.

В пост-сборке

dotnet publish "$(ProjectFileName)" --no-build -o pub
xcopy "$(ProjectDir)pub\3rdPartyProvider.*.dll" "$(OutDir)"

pub это папка, в которой вы хотите разместить опубликованные материалы

ПРИМЕЧАНИЕ: в зависимости от того, какую версию dotnet.exeвы используете, команда --no-buildможет быть недоступна.

Например, недоступно в версии 2.0.3; и доступен в v2.1.402. Я знаю, что VS2017 Update4 имел v2.0.3. И Update8 имеет 2.1.x

Обновить:

Приведенная выше настройка будет работать в базовой среде отладки, но для ее использования в среде сервера сборки / производственной среды требуется больше. В этом конкретном примере, который мне пришлось решать, мы строим Release|x64и Release|x86отдельно. Так что я учел оба. Но для поддержки команды post build dotnet publishя сначала добавил RuntimeIdentifierфайл проекта.

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
  <OutputPath>..\..\lib\</OutputPath>
  <RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
  <OutputPath>..\..\lib\</OutputPath>
  <RuntimeIdentifier>win-x86</RuntimeIdentifier>
</PropertyGroup>

Зачем мне это было нужно и почему без него можно обойтись? Мне это было нужно, потому что моя программа сборки настроена на перехват предупреждения MSB3270 и сбой сборки, если оно появится. Это предупреждение говорит: «Эй, некоторые файлы в ваших зависимостях имеют неправильный формат». Но вы помните цель этого упражнения? Нам нужно вытащить библиотеки зависимостей пакетов. И во многих случаях не имеет значения, есть ли это предупреждение, потому что следующая сборка поста не имеет значения. Опять же, это моя программа сборки, которая заботится. Итак, я добавил только RuntimeIdentifier2 конфигурации, которые использую во время производственной сборки.

Полная сборка поста

if not exist "$(ProjectDir)obj\$(ConfigurationName)" mkdir "$(ProjectDir)obj\$(ConfigurationName)"
xcopy  "$(ProjectDir)obj\$(PlatformName)\$(ConfigurationName)" "$(ProjectDir)obj\$(ConfigurationName)" /E /R /Y

if $(ConfigurationName) == Release (
    dotnet publish "$(ProjectFileName)" --runtime win-$(PlatformName) --no-build -c $(ConfigurationName) -o pub --no-restore --no-dependencies
) else (
    dotnet publish "$(ProjectFileName)" --no-build -c $(ConfigurationName) -o pub --no-restore --no-dependencies
)

xcopy "$(ProjectDir)pub\my3rdPartyCompany.*.dll" "$(OutDir)" /Y /R

Объяснение: dotnet publish ищет obj\Debugили obj\Release. У нас его нет во время сборки, потому что сборка создает obj\x64\Releaseили obj\x86\Release. Строки 1 и 2 смягчают эту проблему. В строке 3 я говорю dotnet.exeиспользовать определенную конфигурацию и целевую среду выполнения. В противном случае, когда это режим отладки, меня не интересуют вещи и предупреждения во время выполнения. И в последней строке я просто беру свои dll и копирую их в выходную папку. Работа выполнена.

TS
источник
Параметр «-c Release» требуется для команды «dotnet publish», если в проекте нет конфигурации отладки (как в моем случае). Итак, я использовал эту партию как событие после сборки: dotnet publish "$(ProjectFileName)" -c Release --no-build -o bin\pub xcopy "$(ProjectDir)pub\PostSharp.dll" "$(OutDir)"
Xtro
0

В сочетании с приведенным выше ответом: у меня это отлично работает в командной строке события после сборки: в Visual Studio. Он перебирает выбранные библиотеки DLL (System * .dll и Microsoft .dll) *, а затем пропускает удаление определенных dll. System.Data.SqlClient.dll и System.Runtime.Loader.dll

for %%f in ($(OutDir)System*.dll $(OutDir)Microsoft*.dll) do if not %%f == $(OutDir)System.Data.SqlClient.dll if not %%f == $(OutDir)System.Runtime.Loader.dll del %%f
морской окунь
источник