I am building a modular .NET core application that can load extensions at runtime using MEF. I have 2 projects, one is a library that I want to be able to load at runtime, then I have my main application that will do the loading.
My library project has some Nuget dependencies. In order to load my library at runtime, I need those Nuget dependencies to be available next to the library at runtime, but building using VS2017 does not include these Nuget DLLs as part of the output.
How do I get Nuget DLLs included when I build my library?
Edit: I have tried dotnet publish and dotnet pack, but both of those make me a nupkg file only containing my DLL and not the nuget DLLs I need with it. Also, I can't load a nupkg file at runtime very easily, which is why I'd like to just get the resulting assemblies themselves on their own.
For what it's worth, this is what my csproj looks like:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<AssemblyName>JSON.plugin</AssemblyName>
<IncludeBuiltProjectOutputGroup>true</IncludeBuiltProjectOutputGroup>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Composition" Version="1.0.31" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\BDDGen.Types\BDDGen.Types.csproj" />
</ItemGroup>
</Project>
In order to make the build process copy all referenced dll files from NuGet packages from the cache folder into the build output, set this property inside a <PropertyGroup>:
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
Related
I can't figure out how should I set up dependencies (where to add EntityFramework nuget packages) in this scenario:
Core.Persistence project which compiles to .NET Standard 2.0 DLL library. I have Entity Framework 6, database entity classes for EF, DbContext etc. It is supposed to depend just on EntityFrameworkCore.
Core.Domain project which also compiles to .NET Standard 2.0 DLL library. I want to put my business object POCO classes here. This is supposed to have no dependencies.
Core.Application project, this is .NET Standard 2.0 DLL library. I have all application logic here. It depends on Core.Persistence because it makes database queries and Core.Domain because it produces bussiness objects from query results.
Client.ConsoleClient project. It makes .NET Framework 4.7.2 executable. It is supposed to depend only on Core.Application, but I have a problem here.
Client.WindowsClient project which I don't want to focus in this question.
So, this is what I have done:
The problem is, that I'm getting System.IO.FileLoadException when I try to call method from Core.Application.
It says that it cannot find System.Interactive.Async file (which is dependency of EntityFrameworkCore). After I add this file as dependency - there are other System.IO.FileLoadException errors.
So, temporarily I have added EF6 Core NuGet package to my Client.ConsoleClient, and problems with System.IO.FileLoadException are gone, but I feel I'm doing something wrong.
At this moment I figured out, that Visual Studio is not copying DLL files from Core.xxx projects outputs into Client.ConsoleClient project output, and that's why I'm getting errors.
How to fix this properly?
This is a wellknown and quite old hurt logged on GitHub at:
dependencies don't flow from new NET Standard project to old desktop projects through project references link
A possible solution is to add the NuGet dependency to the Full NET Framework project, as you did.
The other suggestion to include the following to the .csproj project file of the Full NET Framework project is also working for me.
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
Note that I am using package references in the NET Standard projects.
As for now, it looks like NET Standard projects are best to be consumed as NuGet packages, as these would include any dependent references as NuGet packages into the target project.
Core.Persistence.csproj referencing Entity Framework
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
</ItemGroup>
</Project>
Core.Application.csproj referencing Core.Persistence
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Core.Persistence\Core.Persistence.csproj" />
</ItemGroup>
</Project>
ConsoleClient.csproj referencing Core.Application
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<!-- ... -->
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<!-- ... --->
<ItemGroup>
<ProjectReference Include="..\Core.Application\Core.Application.csproj">
<Project>{067b3201-3f65-4654-a0fb-e8fae521bf29}</Project>
<Name>Core.Application</Name>
</ProjectReference>
</ItemGroup>
</Project>
The new SDK format csproj files don't play terribly well with the legacy format project files.
But fear not as .NET Framework console apps can use the SDK format!
Make sure you have your work committed to source control, or make a copy of the folder, and then do the following:
Delete Properties\AssemblyInfo.cs from Client.ConsoleClient. We won't be needing this any more as the contents of that file now go into the project file.
Delete packages.config - again, Nuget references will be stored in the project file - that's if you need any Nuget references after we reference Core.Application later.
Open Client.ConsoleClient.csproj in a text editor and change the contents to:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
</Project>
Reload the project in Visual Studio.
Add a reference to Core.Application and add any Nuget packages you need.
If you had any content in Properties\AssemblyInfo.cs other than versions at 1.0.0.0, right click the project in Solution Explorer and click Package. Add the details you need and then save:
That's it, although there are 2 others things you might need to do depending on your circumstances:
If there are files which should be excluded, you'll need to exclude them, as the new project format includes all relevant file types by default.
You might have to set the language version. In my Visual Studio 2019 Preview, latest (latest minor version of C#) is the default so I don't need to do this.
I am using Sdk projects targeting .NET Framework 4.7.2. The project structure is:
SmokeTests
|
+--UITests
| |
| +--Common
|
+ NonUITests
|
+--Common
where:
the SmokeTests project references both UITests and NonUITests using ProjectReferences
UITests and NonUITests both reference Common using a ProjectReference
UITests, NonUITests and Common reference some NuGet packages using PackageReferences
the SmokeTests project has no source code, but it does have some content files.
I use the SmokeTests project as a roll up project. When I build it, its bin\debug\net472 directory contains all the binaries and symbols I want to have in the nuget package, i.e.:
The NuGet dependencies of UITests, NonUITests and Common
The dlls of UITests, NonUITests and Common
The PDBs
The SmokeTests csproj looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<PackageId>SmokeTests</PackageId>
<NoPackageAnalysis>true</NoPackageAnalysis>
<IncludeBuildOutput>true</IncludeBuildOutput>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<ContentTargetFolders>content</ContentTargetFolders>
</PropertyGroup>
<ItemGroup>
<Content Include="**\*.ps1" Exclude="PSTests\run.ps1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NonUITests\NonUITests.csproj" />
<ProjectReference Include="..\UITests\UITests.csproj" />
</ItemGroup>
</Project>
I also have a Directory.Build.props file:
<Project>
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AllowedReferenceRelatedFileExtensions>.pdb</AllowedReferenceRelatedFileExtensions>
<Version>$([System.Text.RegularExpressions.Regex]::Match($(BuildName), `\d+\.\d+(?:.\d+)?(?:.\d+)?`))</Version>
<Version Condition="'$(Version)' == ''">1.0.0.0</Version>
<SourceRevisionId>$(Revision)</SourceRevisionId>
<Company>My Company, Inc.</Company>
<Copyright>Copyright © $([System.DateTime]::Now.Year) by My Company, Inc. All rights reserved.</Copyright>
<Product>Smoke Tests</Product>
<NeutralLanguage>en-US</NeutralLanguage>
</PropertyGroup>
</Project>
When I build the solution, the produced SmokeTests.1.0.0.nupkg file does not contain any of the binaries, except the SmokeTests.dll itself. Clearly not what I want.
How can I get everything from bin\debug\net472 into the produced NuGet package without specifying a nuspec file?
I can always hack an after build step that would manipulate the nupkg file, but I want a proper way to do it.
EDIT 1
Judging by the amount of responses either the question is plain stupid or nobody uses .Net core. Posted it here as well - https://social.msdn.microsoft.com/Forums/vstudio/en-US/aa25cb08-ff95-4d81-b0c3-9c4a395f9999/how-to-pack-the-products-from-multiple-projects-into-one-nuget-without-any-nuspec-file?forum=msbuild
Could it be that SO lost its charm?
EDIT 2
I added the following properties to PublicLib.csproj to produce a NuGet package:
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>$(AssemblyName)</PackageId>
<NoPackageAnalysis>true</NoPackageAnalysis>
<IncludeBuildOutput>true</IncludeBuildOutput>
<AllowedOutputExtensionsInPackageBuildOutputFolder>.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
And it almost works, here is the content of the PublicLib.1.0.0.nupkg\lib\netstandard2.0 folder:
But IncludedLib.pdb is missing.
So, after long hours of inspecting the NuGet.Build.Tasks.Pack.targets I have arrived at the following code for my roll up project SmokeTests:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>$(AssemblyName)</PackageId>
<NoPackageAnalysis>true</NoPackageAnalysis>
<IncludeBuildOutput>true</IncludeBuildOutput>
<AllowedOutputExtensionsInPackageBuildOutputFolder>.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<ContentTargetFolders>content</ContentTargetFolders>
<GenerateNuspecDependsOn>MyCustomizePacking</GenerateNuspecDependsOn>
</PropertyGroup>
<Target Name="MyCustomizePacking" Returns="#(NuGetPackInput);#(_BuildOutputInPackage);#(_TargetPathsToSymbols)">
<ItemGroup>
<NuGetPackInput Remove="#(BuiltProjectOutputGroupKeyOutput);#(DebugSymbolsProjectOutputGroupOutput)"/>
<_BuildOutputInPackage Remove="#(BuiltProjectOutputGroupKeyOutput)"/>
<_TargetPathsToSymbols Remove="#(DebugSymbolsProjectOutputGroupOutput)"/>
<NuGetPackInput Include="#(ReferenceCopyLocalPaths);#(AllItemsFullPathWithTargetPath)" />
<_BuildOutputInPackage Include="%(ReferenceCopyLocalPaths.Identity)" >
<TargetFramework>$(TargetFramework)</TargetFramework>
</_BuildOutputInPackage>
<_BuildOutputInPackage Include="%(AllItemsFullPathWithTargetPath.Identity)" >
<TargetFramework>$(TargetFramework)</TargetFramework>
</_BuildOutputInPackage>
</ItemGroup>
</Target>
<ItemGroup>
<Content Include="**\*.ps1" Exclude="PSTests\run.ps1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NonUITests\NonUITests.csproj" />
<ProjectReference Include="..\UITests\UITests.csproj" />
</ItemGroup>
</Project>
The only way I could find to pack all my project and package references into the NuGet was to hook into the process through GenerateNuspecDependsOn by injecting my target MyCustomizePacking. This target does the following:
Remove the SmokeTests.dll and SmokeTests.pdb from the relevant item groups, because this is a roll up project with no code on its own, I do not need its dll or pdb inside the package.
Include #(ReferenceCopyLocalPaths) and #(AllItemsFullPathWithTargetPath) in the relevant item groups.
Seems to work,but I am not happy with my implementation. I wish there was better support for this.
I have a class library which I just migrated to the new csproj format that has a dependency on SQLite.Core.
SQLite has native dependencies x64/SQLite.Interop.dll and x86/SQLite.Interoop.dll which used to get copied to the output folder for any project adding my package. This was taken care of by NuGet by automatically including the following import in the old csproj file:
<Import Project="..\packages\System.Data.SQLite.Core.1.0.106\build\net451\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.106\build\net451\System.Data.SQLite.Core.targets')" />
After migrating to the new csproj I can build my library as a NuGet package which correctly includes SQLite.Core as a dependency however whoever consumes my NuGet library does not get the native dependencies coppied to the output folder.
On the other hand, if the consumer first adds the SQLite.Core then add my package everything works correctly.
Any idea how I can restore the behaviour before the migration? Below is the migrated csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>My.Library</PackageId>
<Description>Some Library.</Description>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net452</TargetFramework>
<AssemblyName>My Library</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.106" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
</Project>
Since System.Data.SQLite.Core uses build-time logic to include the package to the content, the default dependency settings for PackageReference aren't a good fit if you build a library referencing that package. By default, PackageReference will not forward contentFiles, build assets and analyzers for transitive references.
To change that behavior, edit your PackageReference to:
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.106" PrivateAssets="none"/>
This will ensure that the package will behave exactly the same when reference directly or transitively via your library. In your specific case, PrivateAssets could also be contentFiles;analyzers since only build would need to be forwarded but since the package only contains build assets, it doesn't matter.
See Controlling dependency assets for more details.
I am trying to get dotnet pack to generate exact version references when creating NuGet packages. Let's say I have the following class hierarchy:
api.csproj:
<ItemGroup>
<ItemGroup>
<ProjectReference Include="..\api.types\api.types.csproj" />
</ItemGroup>
</ItemGroup>
api.types.csproj has no other dependencies and is simply used for the wire objects.
I have a custom process that updates the Version in all my csproj files prior to restore and build. When I run dotnet pack I would like to be able to control the dependency in api.nupkg such that api.types is an exact version reference. Is there an out of the box way to do this?
I have a solution with two projects in it. First project is called Library1, which references project two called Referencelibrary. I am trying to embed the DLLs for ReferenceLibrary inside Library1's nuget package so that I don't have to publish 2 separate nuget packages. I've been able to embed ReferenceLibrary's DLL into the nuget package (so it seems) by adding the entries below into my csproj file:
<ItemGroup>
<ProjectReference Include="..\ReferenceLibrary\ReferenceLibrary.csproj">
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
<IncludeAssets>ReferenceLibrary.dll</IncludeAssets>
<IncludeAssets>ReferenceLibrary.pdp</IncludeAssets>
</ProjectReference>
</ItemGroup>
But when I import the nuget package and try to run my test app, I get the following exception:
I assumed that the DLLs had been embedded because prior to adding the "IncludeAssets" to the csproj, I wasn't able to import the nuget package because it was trying to reference the ReferenceLibrary nuget package. But after adding those entries, it allowed me to import it. But now it bombs at run-time. Any help would be greatly appreciated. Thanks!
;)
There is now a more up-to-date workaround described here. Simply add the TargetsForTfmSpecificBuildOutput and Target nodes to your .csproj file as shown below.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
</ItemGroup>
</Target>
</Project>
Official documentation for this extension point in the pack target can be found here.
You might also then want to add the attribute PrivateAssets="All" to the ProjectReference element to suppress that project from appearing as a NuGet dependency in the generated package, e.g.:
<ProjectReference Include="MyNonNugetDependentProject.csproj" PrivateAssets="All" />
This appears to be a known limitation with the built-in NuGet packaging for core projects in Visual Studio 2017 and is discussed here:
https://github.com/NuGet/Home/issues/3891
In that discussion thread there is a hack that worked for me:
https://github.com/NuGet/Home/issues/3891#issuecomment-309792369
<ItemGroup>
<_PackageFiles Include="$(OutputPath)\ReferencedProjectDll.dll">
<BuildAction>None</BuildAction>
<PackagePath>lib\net45\</PackagePath>
</_PackageFiles>
</ItemGroup>
Note that you need to change the assembly name AND you might need to also change the package path to match the version of .NET Framework you are using. Above example is for 4.5 but you might be on the more current 4.6.
For placing DLLs within a folder of my choice I used the other customization point as described at Microsoft.
So I ended up with the following:
<PropertyGroup>
<IncludeBuildOutput>false</IncludeBuildOutput> <!-- omit the package creating library itself -->
<PackProject>true</PackProject>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);CreatePackNupkg</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<Target Name="CreatePackNupkg">
<ItemGroup>
<TfmSpecificPackageFile Include="$(OutputPath)\<whatever>.*.dll">
<PackagePath>folder/subfolder</PackagePath>
</TfmSpecificPackageFile>
</ItemGroup>
</Target>
The NuGet.Pack package is created the same way (have a look).
We could not include referenced project DLLs with three or above projects.
For example, the ReferenceLibrary.dll will be added to References for Library1 when project Library1 reference to project ReferenceLibrary. But only the Library1.dll will be add to the References of test app project when you reference project Library1 to project test app. The referenced project DLLs “Referencelibrary” will be omitted . See Flexible Project-to-Project References for more detail.
If you want to embed the DLLs for ReferenceLibrary inside Library1's nuget package and reference it to the test app project, you can add the ReferenceLibrary project reference to test app project after add the reference project Library1
or set ReferenceLibrary.dll as a dependence of Library1 project, you can add the below entries into Library1.csproj, then package the Library1 and install this package to test app via NuGet:
<ItemGroup>
<Reference Include="ReferenceLibrary, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ReferenceLibrary.1.0.0\lib\net461\ReferenceLibrary.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
Update:
If we want to embed the DLLs for ReferenceLibrary inside Library1's nuget package, we should make sure the ReferenceLibrary.dll include in the Library1 package, No matter how we embed the DLLS. So you can add ReferenceLibrary.dll to the Library1.nuspec as file when we pack the Library1 package and set target to the lib folder. Below is my Library1.nuspec:
<?xml version="1.0"?>
<package >
<metadata>
<id>Library1</id>
<version>1.0.0</version>
<authors>xxxx</authors>
<owners>xxxx</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Package description</description>
<releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
<copyright>Copyright 2017</copyright>
<tags>Test</tags>
</metadata>
<files>
<file src="..\Library1\bin\Debug\Referencelibrary.dll" target="\lib\net461" />
<file src="..\Library1\bin\Debug\Library1.dll" target="\lib\net461" />
</files>
</package>
Note that: You also need include the Library1.dll in the Library1.nuspec.
Some extended version of Neo answer. Additionally instruct MSBuild to pack all pdb symbols to chosen symbols format. (built in pdb or snupkg)
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
<TargetsForTfmSpecificDebugSymbolsInPackage>$(TargetsForTfmSpecificDebugSymbolsInPackage);CopyProjectReferencesSymbolsToPackage</TargetsForTfmSpecificDebugSymbolsInPackage>
</PropertyGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
</ItemGroup>
</Target>
<Target Name="CopyProjectReferencesSymbolsToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<SupposedSymbolFiles Include="#(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).pdb')"/>
<TfmSpecificDebugSymbolsFile Include="#(SupposedSymbolFiles)" Condition="Exists('%(Identity)')">
<TargetFramework>$(TargetFramework)</TargetFramework>
</TfmSpecificDebugSymbolsFile>
</ItemGroup>
</Target>