Copy unmanaged DLL from NuGet package to ASP.NET API - c#

My problem is that I have an unmanaged library in a nuget package that I want to copy in the publish folder of the API that embed it.
Here is a StackoverFlow post that I thought it could work as the final goal seems to be the same as me :
Include Unmanaged DLL from Nuget package to web Deploy package
And here is how I tried to adapt it to my project :
<Target Name="AfterBuild" DependsOnTargets="CopyFilesToOutputDirectory">
<ItemGroup>
<MyPackageSourceFile Include="$(SolutionDir)Assemblies\libwkhtmltox\64 bits\libwkhtmltox.dll" />
</ItemGroup>
<Copy SourceFiles="#(MyPackageSourceFile)" DestinationFolder="$(OutputPath)" />
<Copy SourceFiles="#(MyPackageSourceFile)" DestinationFolder="$(ProjectDir)UnmanagedDll" />
</Target>
<Target Name="AddUnmanagedDll" AfterTargets="AfterBuild">
<ItemGroup>
<Content Include="UnmanagedDll\*.dll" />
</ItemGroup>
</Target>
But it doesn't do anything .
In a more common way I also tried something kind of this :
<ItemGroup>
<None Remove="Assemblies\libwkhtmltox\32 bits\libwkhtmltox.dll" />
<None Remove="Assemblies\libwkhtmltox\64 bits\libwkhtmltox.dll" />
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Assemblies\libwkhtmltox\32 bits\libwkhtmltox.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>Assemblies\libwkhtmltox\32 bits\libwkhtmltox.dll</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Assemblies\libwkhtmltox\64 bits\libwkhtmltox.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>Assemblies\libwkhtmltox\64 bits\libwkhtmltox.dll</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
Maybe it could be important to specify that all the projects are in C# .NET CORE 2.1
Please tell me if you want any further information.

Related

Missing assembly reference error in Azure Function

I have created an Azure funtion app but on debug mode it is unable to find below mentioned assembly
System.Private.CoreLib: Could not load file or assembly
'Microsoft.Extensions.Logging.Abstractions, Version=5.0.0.0, Culture=neutral,
PublicKeyToken=adb9793829ddae60'. The system cannot find the file specified.
although I have it referenced, In output folder when I navigate to the properties for this particular file it shows file version "5.0.20.51904"
Below is my project file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Scheduler.Core\**" />
<EmbeddedResource Remove="Scheduler.Core\**" />
<None Remove="Scheduler.Core\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AzureFunctions.Autofac" Version="4.0.1" />
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="SyncFeed_Scheduler.Core\SyncFeed_Scheduler.Core.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
Since you're using .NET 6, but referencing version 5.0.0.0 for Microsoft.Extensions.Logging, there might be a mismatch between the version of your project and the dependencies that were added.
I think this could be solved by either explicitly adding a dependency to version 5.0.0.0 of Microsoft.Extensions.Logging.Abstractions
or
by updating your packages to their latest versions.
For instance: Microsoft.Extensions.Logging has a version 6.0.0.0 available.

Unable to add native .dll reference to source generator project

You can find the full source code at https://github.com/myblindy/GrimBuilding/tree/efcore (the efcore branch).
I understand that source generators can't automatically harvest dependencies from nuget packages and you have to use a clunky work-around to get it to work, and I have done so. This is my source generator project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.0-1.final" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.*-*" GeneratePathProperty="true" PrivateAssets="all" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="6.*-*" GeneratePathProperty="true" PrivateAssets="all" />
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" Version="2.*-*" GeneratePathProperty="true" PrivateAssets="all" />
<PackageReference Include="SQLitePCLRaw.provider.e_sqlite3" Version="2.*-*" GeneratePathProperty="true" PrivateAssets="all" />
</ItemGroup>
<PropertyGroup>
<GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
</PropertyGroup>
<Target Name="GetDependencyTargetPaths">
<ItemGroup>
<TargetPathWithTargetPlatformMoniker Include="$(PKGSQLitePCLRaw_bundle_e_sqlite3)\lib\netstandard2.0\SQLitePCLRaw.batteries_v2.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGSQLitePCLRaw_provider_e_sqlite3)\lib\netstandard2.0\SQLitePCLRaw.provider.e_sqlite3.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGSQLitePCLRaw_lib_e_sqlite3)\runtimes\win-x64\native\e_sqlite3.dll" IncludeRuntimeDependency="false" />
<TargetPathWithTargetPlatformMoniker Include="$(PKGMicrosoft_Data_Sqlite_Core)\lib\netstandard2.0\Microsoft.Data.Sqlite.dll" IncludeRuntimeDependency="false" />
</ItemGroup>
</Target>
</Project>
Since there also isn't any transitive support, I added every nested Microsoft.Data.Sqlite package one by one, generated their path property and referenced it using TargetPathWithTargetPlatformMoniker. It all works until I get to the native e_sqlite3.dll, if I use TargetPathWithTargetPlatformMoniker with it, it tries to reference it as a managed library, and it fails as expected:
4>CSC : warning CS8034: Unable to load Analyzer assembly C:\Users\meep.nuget\packages\sqlitepclraw.lib.e_sqlite3\2.0.5-pre20210521085756\runtimes\win-x64\native\e_sqlite3.dll : PE image doesn't contain managed metadata.
So given that the path is found, is there a different tag I can use to make the main project copy the e_sqlite3.dll file so the analyzer can use it?
Taken from .NET project SDK overview:
Build events
In SDK-style projects, use an MSBuild target named PreBuild or PostBuild and set the BeforeTargets property for PreBuild or the AfterTargets property for PostBuild.
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command=""$(ProjectDir)PreBuildEvent.bat" "$(ProjectDir)..\" "$(ProjectDir)" "$(TargetDir)"" />
</Target>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="echo Output written to $(TargetDir)" />
</Target>
Maybe you can move the file manually using one of these directives?
There's also this:
<Content Include="$(OutputPath)\*.dll;$(OutputPath)\*.json">
<Pack>true</Pack>
<PackagePath>build\</PackagePath>
</Content>
Could you move the file like that?

Creating a NuGet package with .exe file as content file

I have been trying to include the 7za.exe file from the installed 7ZipCLI NuGet package into a project, of which I want a NuGet package to be created. The NuGet package targets both.NET Standard2.0 and .NET Framework v4.6.1+. I am using the Net Standard capability of creating NuGet packages on Build.
The Fonts copy perfectly to the NuGet package, but the .exe does not want to copy.
Can someone please help me get the 7za.exe to be placed in the package's content folder? It does not need to be the content folder, I just want it copied to the consuming project's output directory on Installing of the NuGet package.
Here is the .csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.1.13</Version>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);IncludeAssemblyReferences</TargetsForTfmSpecificBuildOutput>
<Platforms>x86</Platforms>
<PackageId>DocumentGeneration</PackageId>
<Authors>Me</Authors>
<Company>Me</Company>
<Product>DocumentGeneration</Product>
<Description>A library for creating PDF documents.</Description>
<PackageTags>Report Certificate Generator iText PDF</PackageTags>
<AssemblyVersion>1.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
<OutputPath>$(SolutionDir)Output\</OutputPath>
<DocumentationFile>$(SolutionDir)Output\DocumentGeneration.xml</DocumentationFile>
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'">
<OutputPath>$(SolutionDir)Output\</OutputPath>
<DocumentationFile>$(SolutionDir)Output\DocumentGeneration.xml</DocumentationFile>
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
<DebugType>embedded</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Content Include="Resources\Arial.ttf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
<CopyToPublishDirectory>true</CopyToPublishDirectory>
</Content>
<Content Include="Resources\HelveticaNarrow.otf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
<CopyToPublishDirectory>true</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<PackageReference Include="iTextSharp" Version="5.5.13.2" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="itextsharp.netstandard" Version="5.5.13.2" GeneratePathProperty="true" />
<PackageReference Include="System.Drawing.Common" Version="5.0.0" GeneratePathProperty="true" />
<PackageReference Include="System.Resources.Extensions" Version="5.0.0" />
<PackageReference Include="System.Resources.ResourceManager" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="7ZipCLI" Version="9.20.0" GeneratePathProperty="true" IncludeAssets="all" />
<PackageReference Include="System.Text.Encoding" Version="4.3.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<None Include="readme.txt" pack="true" PackagePath="." />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<None Include="$(Pkgitextsharp_netstandard)\lib\netstandard2.0\itextsharp.netstandard.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
<Visible>false</Visible>
</None>
<Content Include="$(Pkg7ZipCLI)\tools\7za.exe">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
<PackagePath>content\Resources;contentFiles\any\any\Resources</PackagePath>
<Visible>false</Visible>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
<Content Include="$(Pkg7ZipCLI)\tools\7za.exe">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
<PackagePath>content\Resources;contentFiles\any\any\Resources</PackagePath>
<Visible>false</Visible>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Target Name="IncludeAssemblyReferences">
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<BuildOutputInPackage Include="$(Pkgitextsharp_netstandard)\lib\netstandard2.0\itextsharp.netstandard.dll">
<Visible>false</Visible>
</BuildOutputInPackage>
</ItemGroup>
</Target>
</Project>
Also here is what the NuGet package looks like currently:
If you just want the file be output into the bin folder under packages.config, you should have to use <packages_id>.props file and <PackageCopyToOutput>true</PackageCopyToOutput> is only for PackageReference.
Also, <CopyToOutputDirectory>Always</CopyToOutputDirectory> is only for local file under the local project rather than the nuget package. And it does not work for nuget package.
Solution
1) add a file called DocumentGeneration.props(<packages_id>.props and your packageID is DocumentGeneration) under build folder
2) modify your DocumentGeneration.csproj file:
.....
<Content Include="$(Pkg7ZipCLI)\tools\7za.exe">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
<PackagePath>content\Resources;contentFiles\any\any\Resources</PackagePath>
<Visible>false</Visible>
</Content>
<None Include="build\*.*">
<Pack>true</Pack>
<PackagePath>build</PackagePath>
</None>
.....
3) add these on the DocumentGeneration.props file:
<Project>
<ItemGroup>
<None Include="$(ProjectDir)Resources\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Or
<Project>
<None Include="$(MSBuildThisFileDirectory)..\content\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>Resources\%(FileName)%(Extension)</Link>
</None>
</Project>
4) repack your lib project. When you install this new release version, you should first clean nuget caches or delete all cache files under C:\Users\xxx(current user)\.nuget\packages.
Besides, there is a similar issue.
Update
I think it is an issue for multi-targetframeworks feature. And if you use GeneratePathProperty for the pkg, the property will lost on the other targetframework. It is quite strange and when you change to use <TargetFramework>, it works well. Since the issue is under TargetFrameworks with GeneratePathProperty=true. I have no other good idea for that and cannot find the good solution to fix them.
And you should report it on DC Forum or raise an issue on github. And when you finish it, you could share it here.
1) Also, since you use multi-targetframeoworks, you should also use buildCrossTargeting folder on the nupkg to enable the props file. See this similar issue.
2) Second, when I use these to distinguish between them, nuget also cannot pack it. And it is so strange, and I have to remove that condition.
<ItemGroup Condition="'$(TargetFramework)'=='net461'">
...
<None Include="7za.exe" Pack="true" PackagePath="content\Resources;contentFiles\any\any\Resources">
...
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
...
<None Include="7za.exe" Pack="true" PackagePath="content\Resources;contentFiles\any\any\Resources">
...
</ItemGroup>
My thought is that content and contentFiles node target to the whole project and you cannot specify a condition with targetframeworks during packing them as content. And what I know is that content and contentFiles are targets to the whole and cannot target to a targetframework under multi-targetframeworks.
You should use these:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
</PropertyGroup>
...
<ItemGroup>
<PackageReference Include="7ZipCLI" Version="9.20.0" GeneratePathProperty="true">
</PackageReference>
</ItemGroup>
...
<Target Name="FunCopy" BeforeTargets="PrePareForBuild">
<Copy SourceFiles="$(Pkg7ZipCLI)\tools\7za.exe" DestinationFolder="$(ProjectDir)"></Copy>
</Target>
<ItemGroup>
<None Include="$(ProjectDir)7za.exe" Pack="true" PackagePath="content\Resources;contentFiles\any\any\Resources">
<PackageCopyToOutput>true</PackageCopyToOutput>
<Visible>false</Visible>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Include="build\*.*" Pack="true" PackagePath="build;buildCrossTargeting"></None>
</ItemGroup>
...
</Project>

Publishing [part] of ASP.NET MVC Core application to web host

The situation is such, I am able to successfully publish my application live to the web host via Visual Studio 2018 FTP publishing option and it connects and publishes it correctly and the changes are visible.
However, there are a few folders which are stored on the server which contain several audio files, so every time I publish the audio files are deleted and and than republished. Not only is the process too long, it is redundant. And since and ASP.NET Core converts the application to dll files, it is faster on the clients end, but posting changes is a nightmare, since all the files have to be deleted including the audio folder and re-uploaded.
The csproj file looks like this :
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Content Remove="Views\Shared\ExplainPage.cshtml" />
</ItemGroup>
<ItemGroup>
<None Remove="Properties\PublishProfiles\CustomProfile.pubxml" />
<None Remove="Properties\PublishProfiles\CustomProfile1.pubxml" />
<None Remove="Properties\PublishProfiles\umaraftab.net - Web Deploy.pubxml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="2.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\PublishProfiles\" />
<Folder Include="wwwroot\explanations\" />
<Folder Include="wwwroot\lectures\" />
</ItemGroup>
<ItemGroup>
<None Include="wwwroot\slick\fonts\slick.svg" />
</ItemGroup>
</Project>
The wwwroot\lectures and wwwroot\explanation folders contain the audio files.
Would anyone be able to advice on how I can just let the audio folders stay as-is on the server and just recreate the dll files and upload them, as that is where the changes are.
Regards,
Try replacing those 2 folders in the csproj with the remove keyword :
<Folder Remove="wwwroot\explanations\" />
<Folder Remove="wwwroot\lectures\" />
And during publish, do not delete files in the destination folder.
You can exclude these folders from the project. Update the .csproj file to remove the following two lines of code:
<Folder Include="wwwroot\explanations\" />
<Folder Include="wwwroot\lectures\" />
You can edit the .csproj directly inside Visual Studio by right clicking project node in solution explorer and then edit.

Copy files from Nuget package to output directory with MsBuild in .csproj and dotnet pack command

Last time I had to find out how to extract some files from a Nuget package in took me at least 6 months but I finally managed to find the solution.
The thing is that, this solution assumes I have a .nupkg file and manually add a .targets file to perform the extraction process.
Now, things are different:
I don't have any .nupgk file, we generate one automatically on our VSTS server using the dotnet pack command. Then we consume the package from our Nuget server
We can't afford to take another 6 months to find the solution
Here is my ProjectName.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<Authors>Jérôme MEVEL</Authors>
<Version>1.0.3</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<!-- This answer got many votes on a Github thread so I tried just in case -->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- Just trying that too-->
<IncludeBuildOutput>true</IncludeBuildOutput>
<IncludeContentInPack>true</IncludeContentInPack>
<!-- I wanted to see the full generated Nuget package -->
<IncludeSource>true</IncludeSource>
<!-- desperate attempt -->
<TargetsForTfmSpecificBuildOutput>GetMyPackageFiles</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="1.50.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
<PackageReference Include="NLog" Version="4.5.8">
<!-- Let's try this too just in case-->
<IncludeAssets>all</IncludeAssets>
</PackageReference>
<PackageReference Include="NLog.Extensions.Logging" Version="1.2.1">
<IncludeAssets>all</IncludeAssets>
</PackageReference>
<PackageReference Include="NLog.Web.AspNetCore" Version="4.6.0">
<IncludeAssets>all</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.5.1" />
</ItemGroup>
<ItemGroup>
<!-- Added the file to the root folder in case <IncludeAssets>all</IncludeAssets> is looking there -->
<Content Include="NLog.config">
<Pack>true</Pack>
<PackagePath>NLog;;</PackagePath>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<!-- My desired path to look for the file -->
<Content Include="NLog\NLog.config">
<Pack>true</Pack>
<PackagePath>NLog;;</PackagePath>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<!-- This is the default setting, perfectly working when I reference the project instead of installing the Nuget package -->
<ItemGroup>
<None Update="NLog\NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<!-- desperate attempt -->
<Target Name="GetMyPackageFiles">
<ItemGroup>
<BuildOutputInPackage Include="NLog/Nlog.config">
<TargetPath>NLog</TargetPath>
</BuildOutputInPackage>
</ItemGroup>
</Target>
</Project>
As you can see I tried several different settings. This MsBuild results in a NLog.config file included in a NLog folder at the root of the Nuget package file.
During my different tries, depending of the configuration I set, I was able to end-up with the NLog.config file at src/ProjectName.Logging/NLog/NLog.config or even at lib/netstandard2.0/Nlog.config.
So my file is definitely included in my Nuget package file but isn't copied in the output directory of the project that consumes the package.
I tried to specify a .nuspec file when generating my package with dotnet pack like described here but I was never able to get a desired result (either only my NLog.config was included in the Nuget package or all the source files). Moreover, this has several downsides like overriding the configuration in the .csproj file or adding useless complexity. I believe what I want to achieve could be done without using a .nuspec file (maybe I'm wrong).
I noticed the build/ProjectName.targets file is missing in my package and this is probably the missing piece. So how to add this .targets file without manually modifying the package?
Is there another way to copy my config file out of the Nuget package to the output directory?
I really hope someone could help me solve this issue. This is the 2nd time I want to perform the same operation but with a slight difference and once again this is hard to do.
Thanks a lot
It is possible to copy files without the .nuspec file, if you create your nuget from the .csproj file as described here.
And to copy files from nuget to output directory, create a ProjectName.targets file with the following content:
<ItemGroup>
    <LogFiles Include="$(MSBuildThisFileDirectory)\..\contentFiles\LogFiles\*.config" />
</ItemGroup>
<Target Name="CopyLogFiles" BeforeTargets="Build">
    <Copy SourceFiles="#(LogFiles)" DestinationFolder="$(TargetDir)CopiedLogFiles\" />
</Target>
In your .csproj file add:
<ItemGroup Label="FilesToCopy">
   <Content Include="ProjectName.targets" PackagePath="build/ProjectName.targets" />
   <Content Include="LogFiles\*.config" Pack="true" PackagePath="contentFiles\LogFiles">
     <PackageCopyToOutput>true</PackageCopyToOutput>
   </Content>
</ItemGroup>
The paths and names can of course be freely chosen.
This should copy all .config files to a folder called CopiedLogFiles in the output directory!
Ok I finally found the solution and that includes a .nuspec file and a .targets file as well.
The ProjectName.csproj just needs to include this
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<NuspecFile>ProjectName.Logging.nuspec</NuspecFile>
</PropertyGroup>
<!-- This is just for some projects referencing this project directly instead of the Nuget package -->
<ItemGroup>
<Content Include="NLog\NLog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="1.50.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
<PackageReference Include="NLog" Version="4.5.8" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.2.1" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.6.0" />
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.5.1" />
</ItemGroup>
</Project>
In the ProjectName.nuspec you put everything related to the Nuget package
<?xml version="1.0"?>
<package >
<metadata>
<id>ProjectName.Logging</id>
<version>1.1.0</version>
<authors>Jérôme MEVEL</authors>
<description>Just a logging component</description>
<releaseNotes>Extract the NLog.config file automatically</releaseNotes>
<dependencies>
<dependency id="Dapper" version="1.50.5" />
<dependency id="Microsoft.Extensions.DependencyInjection" version="2.1.1" />
<dependency id="Microsoft.Extensions.Logging" version="2.1.1" />
<dependency id="Microsoft.Extensions.Logging.Abstractions" version="2.1.1" />
<dependency id="NLog" version="4.5.8" />
<dependency id="NLog.Extensions.Logging" version="1.2.1" />
<dependency id="NLog.Web.AspNetCore" version="4.6.0" />
<dependency id="System.Data.Common" version="4.3.0" />
<dependency id="System.Data.SqlClient" version="4.5.1" />
</dependencies>
</metadata>
<files>
<file src="bin\Release\netstandard2.0\ProjectName.Logging.dll" target="lib/netstandard2.0/ProjectName.Logging.dll" />
<file src="ProjectName.Logging.targets" target="build/ProjectName.Logging.targets" />
<file src="NLog/Nlog.config" target="content/Nlog.config" />
</files>
</package>
And finally the ProjectName.targets. Careful! The file is located in the Nuget cache of the machine. You will be able to see it at the root of your project in Visual Studio but not in the Windows Explorer (in Windows at least). So if you modify the file in Visual Studio, it will modify it for ALL other projects on this machine referencing the same Nuget package (and same version).
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Content Include="$(MSBuildThisFileDirectory)\..\content/NLog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
I generate the Nuget package using the dotnet pack nuget pack command (now that I've more experience I know that dotnet pack doesn't work well with a .nuspec file, there are several bugs)
Here is the result:
Finally I can just install my package and during the build process, the Nlog.config file will be copied out of the Nuget cache to the output directory of my project.
I think that How do you set nuget contentFiles CopyToOutput value to true when using a .Net Standard library .csproj? provides a better answer to this question.
https://github.com/NuGet/NuGet.Client/pull/1450
You can set PackageCopyToOutput to true in the .csproj to declare the nuget content file as "CopyToOutput=true". That way any project referencing the nuget will have the content file marked with <CopyToOutput>true</CopyToOutput> in the referring csproj file, instructing msbuild to copy the content file to the ouput directory:
In the .csproj of the nuget project:
<Content Include="...">
<PackageCopyToOutput>true</PackageCopyToOutput>
</Content>

Categories

Resources