How to build .xproj before .csproj - c#

This is a follow-up from this question.
As I'm not able to directly reference my .xproj in my .csproj in Visual Studio, I am forced to directly reference the built net461 dll. So in this situation, I want my .csproj to pre-compile the .xproj before running the .csproj itself.
I've tried modifying the .csproj manually and adding:
<ItemGroup>
<ProjectReference Include="..\SomeFolder\SomeProject.xproj">
<Project>{1A94B405-2D01-4A09-90D5-A5B31180A03B}</Project>
<Name>SomeProjectNamespace</Name>
</ProjectReference>
</ItemGroup>
But when I build the .csproj, it doesn't build the .xproj.
I won't be able to debug .xproj when I run my .csproj, which is bad enough... but at least when I run the .csproj I want to be sure I have the last version of my .xproj compiled.

xproj <=> csproj references in Visual Studio 2015 were always buggy and never worked well. As far as I know, there are no good workarounds.
That said, Visual Studio 2017 RC added support for building .NET Core and .NET Standard with csproj. This deprecates xproj and project.json.
With this new format, a project to project reference would look like this:
<!-- App.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Library\Library.csproj" />
</ItemGroup>
</Project>
<!-- Library.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
</PropertyGroup>
</Project>

Related

System.IO.FileNotFoundException after changing TargetFramework to net6.0 while using assembly reference

I have a project A that in its csproj has TargetFramework set to net48 and uses a local dll from another project B for an assembly reference. If I build both of the projects and run the app A it works correctly, and the bin folder contains every dll for Nuget packages the project B needs.
When I change TargetFramework of both of the projects to net6.0 and build them, the bin folder of the project A doesn't contain the needed dlls anymore, and if i run the app A it stops with the exception: "System.IO.FileNotFoundExceptionn: Could not load file or assembly".
Here's an example of what the project A's csproj looked like before:
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<LangVersion>latest</LangVersion>
<RootNamespace>ProjectA</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Reference Include="ProjectB">
<HintPath>..\..\SolutionB\ProjectB\bin\Release\ProjectB.dll</HintPath>
</Reference>
</ItemGroup>
And here's how it looks after switching to net6.0 (I'm including other minor changes in case it turns out to be important):
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<LangVersion>latest</LangVersion>
<RootNamespace>ProjectA</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Reference Include="ProjectB">
<HintPath>..\..\SolutionB\ProjectB\bin\Release\net6.0\ProjectB.dll</HintPath>
</Reference>
</ItemGroup>
I couldn't find the answer for why it behaves differently and what is needed to achieve the same behaviour as before, if it's even possible. If not then what is the best way to reference another project's assembly? Would really appreciate the help on this.

Cannot find DLL from Project Reference in dotnetcore 5 with F5 or release / publish builds

I have a csproj file with nothing out of the ordinary
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>AppService</RootNamespace>
<AssemblyName>AppService</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference ..../>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Datastore\Datastore.csproj" />
</ItemGroup>
</Project>
Note the ProjectReference on the third line from the bottom of the file -
Both projects build fine - if I run dotnet build followed by dotnet run, everything is fine - however, if I try to run the code generated by the publish profile or even F5 - I get a CLR binding error:
I don't understand what is going on nor where to look. It's not an incorrect format error like many of the other questions.

Building multiple framework support in Visual Studio by one solution per framework

I want to support multiple TargetFrameworks in my Visual studio Microsoft.NET.Sdk project, but I want to make it so that I would have several solutions, where solution would determine which target framework it will compile.
Theoretically this can be done for example like this:
Directory.Build.props:
<Project>
<!--
TargetFramework is not defined at nuget restore phase - but we can define it.
If solution name contains "netcore" - then solution aims to .net core.
-->
<PropertyGroup Condition="$(SolutionName.Contains('netcore'))">
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="!$(SolutionName.Contains('netcore'))">
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<!-- nuget restore path must be different for different target frameworks, otherwise it will create mess between projects -->
<BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)obj\nuget\$(TargetFramework)\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
</PropertyGroup>
</Project>
and then to have two solutions, one for .net framework 4.7.2, one for .net core, where second solution filename should contain 'netcore' string in it.
This however will not work pretty in case if TargetFramework starts to vary also between netstandard2.0 and so on...
Then it came to my mind - that maybe I could perform nuget restore for multiple frameworks, but just to compile project for one framework. I think BeforeBuild event could be used to differentiate build phase between nuget restore and build - so I wrote something like this:
test.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
</PropertyGroup>
<Target Name="BeforeBuild">
<PropertyGroup>
<TargetFrameworks>net472</TargetFrameworks>
</PropertyGroup>
<Message Text="TargetFrameworks: $(TargetFrameworks)" />
</Target>
But this does not work, build happens for two frameworks, not for one, like I've desired originally.
Of course BeforeBuild should be eventually executed for solution, which does not have netcore in it's name.
Maybe someone could propose better solution for this one ?
Figured out.
So multiple solutions will not tolerate each other presence, and nuget restore directory needs to be overridden. Override can be done per solution basis, just to put things simple - just use solution name to define nuget restore path.
Directory.Build.props:
<Project>
<PropertyGroup>
<BaseIntermediateOutputPath>$(MSBuildThisFileDirectory)obj\nuget\$(SolutionName)\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
</PropertyGroup>
</Project>
And individual projects then determine target framework, which they want to use, for example like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup Condition="$(SolutionName.Contains('netcore'))">
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="!$(SolutionName.Contains('netcore'))">
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<IntermediateOutputPath>$(SolutionDir)obj\$(TargetFramework)-$(Configuration)\$(AssemblyName)\</IntermediateOutputPath>
<OutputPath>$(SolutionDir)bin\$(TargetFramework)-$(Configuration)</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Remove="obj\**" />
<EmbeddedResource Remove="obj\**" />
<None Remove="obj\**" />
</ItemGroup>
</Project>
This will allow to have separate solutions, one for each target framework, but also will work correctly for nuget restore + build.

How to pack the products from multiple projects into one nuget without any nuspec file?

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.

Target .NET 4.x and .NET Standard 2.0

Last year I added .NET Standard 2.0 support to the Network library. I did achieve this by creating a second (.NET Standard) project, and basically copy + paste the sourcecode. With some adjustments it was ready to go.
But since I add features on demand, it is really bothersome to change the same thing in both projects. It would be great to just create one code-base and simply change the compile target.
Pre-Compile statements aren't an option, because the .NET 4.x version does additionally include some NuGet packages, which aren't available for .NET Standard.
The solution I can currently think of is, to create a shared library, including all the cross-project classes. Or is there a much smoother solution?
Solved the Problem with the suggested solution. The .csproj Looks like following
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net46;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Remove="packages\**" />
<EmbeddedResource Remove="packages\**" />
<None Remove="packages\**" />
</ItemGroup>
<ItemGroup>
<!-- PackageReferences for all TargetFrameworks -->
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net46' ">
<!-- PackageReferences for net46 TargetFramework -->
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' ">
<!-- PackageReferences for standard2.0 TargetFramework -->
</ItemGroup>
</Project>
The only issue currently: I can't use the NuGet Package-Manager. I have to add every entry manually into the correct ItemGroup.
EDIT: The manual edit is only required if the packages are not supported by both TargetFrameworks. Simply Change in the Settings -> NuGet-Paket-Manager -> Default Format -> PackageReference
You can add source files from an existing project to another project as a link.
project a
somefile.cs
project b
Right click on project b, add existing item...navigate to somefile.cs in project a, and then add
You can edit the file from either project...so be careful.

Categories

Resources