ChatGPT解决这个技术问题 Extra ChatGPT

如何让 .NET Core 项目将 NuGet 引用复制到构建输出?

我正在尝试使用 .NET Core 编写插件系统,我的要求之一是能够将插件 DLL 及其依赖项分发给用户进行安装。

但是,我不知道如何将我的 NuGet 依赖项包含为构建工件并将它们输出到构建文件夹,而不必使用 dotnet publish 作为 hack。有什么方法可以在 .csproj 文件(项目文件)中指定吗?

为什么使用 dotnet publish 是一种黑客行为?在您的 csproj 文件中包含该命令作为构建后脚本。
dotnet publish 将整个框架放在发布文件夹中,因为我正在编写插件,所以大部分文件都不是必需的,因为框架已经由引导程序加载。我正在寻找类似于构建在 .NET Framework 上的工作方式的东西。
<PackageReference/> 不支持 <CopyToOutputDirectory>
“整个框架”来自 NuGet。如果您选择将所有 NuGet 程序集复制到构建输出,您将获得所有这些程序集。
@AustinDrenski - 我尝试了你的建议,但我强烈反对将 dotnet publish 添加为构建后事件,因为 dotnet publish 也在重建导致构建递归的项目。

W
Wai Ha Lee

您可以将其添加到您的 csproj 文件中的 <PropertyGroup>,以强制将 NuGet 程序集复制到构建输出:

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

但是,请注意,构建输出 (bin/Release/netcoreapp*/*) 不应该是可移植和可分发的,dotnet publish 的输出是。但是在您的情况下,将程序集复制到构建输出对于测试目的可能非常有用。但请注意,您也可以使用 DependencyContext api 来解析作为应用程序依赖关系图一部分的 DLL 及其位置,而不是枚举本地目录。


它会导致复制所有 dll,而不仅仅是 Nuget dll
Core 2 我也得到了所有的 Microsoft DLL。不知道为什么,但在我只得到 NuGet 之前它就停止了?恼人的
@MartinUllrich 您能否详细说明DependencyContext?如何使用它来查找不在应用程序目录中的 DLL?它到底在哪里?
只要这些包“做正确的事情”,VS 测试运行器就应该使用正确的包。使用 dotnet new nunit 创建 NUnit 测试项目。
不适合我 asp.net 核心不复制 System.ValueTuple.dll
X
Xeevis

您可以使用 PostBuildEvent 在构建时自动部署模块。

要在构建文件夹中获取 NuGet 程序集,请添加模块的 csproj

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

使用包含/排除定义您想要的模块文件(根据需要修改路径)

<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 来回收应用程序,以避免文件使用中的错误。


在我的项目中,我依赖于“Microsoft.Extensions.Logging.Log4Net.AspNetCore”Nuget 库,它不是 NetCore 的一部分,所以这种方法不起作用
M
Mike Brunner

添加

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

没用,但将其添加到 Framework .csproj 文件中:

<RestoreProjectStyle>PackageReference</RestoreProjectStyle>

做过。


当我从 .Net Framework 4.7.2 项目中引用 .net Standard 2.0 库时,这对我很有效。没有其他东西可以解决它。
在我的情况下,我使用 Serilog、Serilog.Console、Serilog.Files 作为对项目 A 的 nuget 引用,而项目 B 引用项目 A。如果没有 RestoreProjectStyle 元素,则会复制一些 Serilog、Serilog.Console,但不会复制 Serilog.Files.dll复制。将 添加到项目 A、项目 B 后,它开始工作。我想你只需要项目A。
这对我也不起作用
J
Jason Tarr

我正在使用 .NET 5,这是我对类似问题的解决方案。

结构:Project-A(Contained Selenium Nuget References, and selenium code)Project-B(一个单元测试项目,调用Project-A中的方法)

问题:在构建解决方案时,chromedriver.exe 文件出现在 Project-A bin 文件夹中,但不会被复制到 Project-B bin 文件夹中,因此无法执行单元测试。抛出一个异常说 chromedriver.exe 没有找到。

解决方案:修改 Project-A 中 Selenium ChromeDriver NuGet 包引用的属性,仅将“contentfiles;analyzers”视为私有资产。未指定时,默认值为 'contentfiles;analyzers;build'。现在这意味着可以将构建的输出文件流向父引用项目,但不能流向内容文件或分析器,因为“构建”以前也被认为是私有资产,不会流向父项目。

之前(在 Project-A.csproj 中):

<ItemGroup>
  <PackageReference Include="Selenium.Support" Version="3.141.0" />
  <PackageReference Include="Selenium.WebDriver" Version="3.141.0" />
  <PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="87.0.4280.8800" />
</ItemGroup>

之后(在 Project-A.csproj 中):

<ItemGroup>
  <PackageReference Include="Selenium.Support" Version="3.141.0" />
  <PackageReference Include="Selenium.WebDriver" Version="3.141.0" />
  <PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="87.0.4280.8800">
    <PrivateAssets>contentfiles;analyzers</PrivateAssets>
  </PackageReference>
</ItemGroup>

我在此链接中找到了此信息:https://docs.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets

希望这对某人有帮助!祝你好运。


T
T.S.

我以更简单的方式“解决”(创建了解决方法)这个问题。

在后期构建中

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

pub 是您希望发布的内容用于暂存的文件夹

注意:根据您使用的 dotnet.exe 版本,命令 --no-build 可能不可用。

例如,在 v2.0.3 中不可用;并在 v2.1.402 中可用。我知道 VS2017 Update4 有 v2.0.3。 Update8 有 2.1.x

更新:

上面的设置将在基本的调试环境中工作,但要将其放入构建服务器/生产环境中还需要更多。在我必须解决的这个特定示例中,我们分别构建 Release|x64Release|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,如果出现则构建失败。此警告说,“嘿,您的依赖项中的某些文件格式错误”。但是你还记得这个练习的目标吗?我们需要提取包依赖 DLL。在许多情况下,是否存在此警告并不重要,因为后续构建无关紧要。同样,这是我关心的构建程序。因此,我只在生产构建期间使用的 2 个配置中添加了 RuntimeIdentifier

完整的后期构建

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\Debugobj\Release。我们在构建期间没有它,因为构建会创建 obj\x64\Releaseobj\x86\Release。第 1 行和第 2 行缓解了这个问题。在第 3 行中,我告诉 dotnet.exe 使用特定的配置和目标运行时。否则,当这是调试模式时,我不关心运行时的东西和警告。在最后一行中,我只需将我的 dll 复制到输出文件夹中。任务完成。


如果项目没有调试配置(如我的情况),“dotnet publish”命令需要“-c Release”参数。所以我将此批次用作构建后事件:dotnet publish "$(ProjectFileName)" -c Release --no-build -o bin\pub xcopy "$(ProjectDir)pub\PostSharp.dll" "$(OutDir)"
s
seabass

结合上面的答案:我在生成后事件命令行中很好地工作:在 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