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

How do I get .NET Core projects to copy NuGet references to the build output?

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

但是,我无法弄清楚如何将我的 NuGet 依赖项包含为构建工件并将它们输出到构建文件夹,而不必使用 dotnet publish 作为 hack。我可以通过某种方式在 .csproj 文件(项目文件)中指定它吗?

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

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

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

您可以使用 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 以回收应用程序。

I "solved" (created work around) this in simpler way.

In post build

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

pub is the folder where you want your published stuff go for staging

NOTE: depending on what version of dotnet.exe you use, command --no-build may not be available.

For example, not available in v2.0.3; and available in v2.1.402. I know that VS2017 Update4 had v2.0.3. And Update8 has 2.1.x

Update:

The setup above will work in the basic debug environment but to put it into build server/production environment more is needed. In this particular example that I had to solve, we build Release|x64 and Release|x86 separately. So I accounted for both. But to support the post build dotnet publish command, I first added RuntimeIdentifier to project file.

<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>

Why I needed it and why you can get away without it? I needed this because my build program is set to intercept warning MSB3270, and fail the build if it appears. This warning says, "hey, some files in your dependencies are of wrong format". But do you remember the goal of this exercise? We need to pull package dependency DLLs. And in many cases it doesn't matter if this warning is there because following post build does not care. Again, this is my build program that cares. So, I only added RuntimeIdentifier to 2 configurations I use during production build.

Full Post build

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

Explanation: dotnet publish is looking for obj\Debug or obj\Release. We don't have it during the build because build creates obj\x64\Release or obj\x86\Release. Line 1 and 2 mitigate this issue. In line 3 I tell dotnet.exe to use specific configuration and target runtime. Otherwise, when this is debug mode, I don't care about runtime stuff and warnings. And in the last line I simply take my dlls and copy then into output folder. Job done.

结合上面的回答: 我在 Post-build 事件命令行中非常有效:Visual Studio 中的。 它循环选择 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

添加

<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

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

<RestoreProjectStyle>PackageReference</RestoreProjectStyle>

做了。

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

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

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

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

之前(在项目-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>

之后(在项目中-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>

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

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