【问题标题】:How do I get .NET Core projects to copy NuGet references to the build output?如何让 .NET Core 项目将 NuGet 引用复制到构建输出?
【发布时间】:2017-10-05 20:46:02
【问题描述】:

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

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

【问题讨论】:

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

标签: c# msbuild .net-core nuget csproj


【解决方案1】:

您可以将其添加到您的 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 core not copying System.ValueTuple.dll
【解决方案2】:

您可以使用 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 的一部分,所以这种方法不起作用
【解决方案3】:

添加

<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。
  • 这对我也不起作用
【解决方案4】:

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

结构: Project-A(包含 Selenium Nuget 参考和 selenium 代码) Project-B(单元测试项目,调用Project-A中的方法)

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

解决方案: 修改 Project-A 中 Selenium ChromeDriver NuGet 包引用的属性,以仅将“内容文件;分析器”视为私有资产。未指定时,默认值为 '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

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

【讨论】:

    【解决方案5】:

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

    在后期构建中

    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。在许多情况下,是否存在此警告并不重要,因为后续构建无关紧要。同样,这是我关心的构建程序。因此,我只将RuntimeIdentifier 添加到我在生产构建期间使用的 2 个配置中。

    完整的后期构建

    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\pubxcopy "$(ProjectDir)pub\PostSharp.dll" "$(OutDir)"
    【解决方案6】:

    结合上面的答案: 我在 Visual Studio 中的 Post-build 事件命令行 中运行良好。 它循环选择 dll (System*.dll 和 Microsoft.dll)*,然后跳过特定 dll 的删除。 System.Data.SqlClient.dllSystem.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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-09
      • 1970-01-01
      • 1970-01-01
      • 2020-06-22
      • 2019-01-28
      • 2016-03-01
      • 2023-03-03
      相关资源
      最近更新 更多