I'm working on a (very) large C# project with a significant number of external packages. I'm trying mitigate the problem of unnecessary merge conflicts in *.csproj files caused by package updates.
The new style of PackageReference that has a Version attribute is very prone to merge conflicts if packages are updated on adjacent lines:
<PackageReference Include="Package1" Version="1.0.0" />
<PackageReference Include="Package2" Version="1.0.0" />
<PackageReference Include="Package3" Version="1.0.0" />
Given a csproj file with the above content, if one developer bumps the version of Package1 and Package3 while another bumps Package2, the two conflict. This requires unnecessary manual work although no real conflict has occurred - all three packages have been bumped.
So far, I explored three possible solutions, but haven't found a clear winner:
Setting a diff driver for *.csproj files using .gitattributes. However, none of the built-in diff drivers seem to produce a cleaner diff result (not surprising, given none of them is meant for csproj or even xml files).
Forcing the usage of the old style of PackageReference, in which Version isn't an attribute but an xml element: <Version>1.0.0</Version>. This causes significantly fewer conflicts due to the extra lines. However, I couldn't find a clear way of forcing VS/Rider/Nuget to use the old style, so we'd need to distribute a custom git hook to everyone working on the project to force it.
Distributing the most frequently updated packages to external .targets files, then importing these files using <Import>. This works, but requires custom tooling, especially for adding new references in a consistent manner. This also seems to (partially) break the "Manage NuGet" functionality of both VS (2022) and Rider, though that isn't a big deal.
Can anyone suggest other approaches or ways to improve on the ones above?
For anyone in the same boat, we ended up going with CentralPackageVersions with packages.props being in the "long" format in which Version is an element, not an attribute:
<PackageReference Update="Foo">
<Version>1.0.0</Version>
</PackageReference>
It's also worth pointing out that Microsoft recently introduced Central Package Managemet which is supposed to be supported by Visual Studio and other tooling. However, it requires new versions of just about everything:
The feature is available across all NuGet integrated tooling.
Visual Studio 2022 17.2 and later
.NET SDK 6.0.300 and later
.NET SDK 7.0.0-preview.4 and later
nuget.exe 6.2.0 and later
I have a Nuget package project that targets framework netcoreapp2.2. I'm trying to use it for an application that contains other projects targeting frameworks netstandard2.0;netcoreapp3.1.
This is what I currently have in the .csproj for that
<PropertyGroup>
<TargetFrameworks>netcoreapp2.2;netstandard2.0;netcoreapp3.1</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework) '=='netcoreapp2.2'">
( ........ )
In the classes I get the conflicts I'm using the conditional directive
##if true
1. But I'm getting compile errors because some of the extensions I'm using here are not available in one of the frameworks above.
*[Solved] I get this error:
Error CS0006 Metadata file 'C:\Users\user\source\repos\workspace\project\bin\Debug\netstandard2.0\project.dll' could not be found project.Test (netcoreapp2.2)
Is there any way I can target multiple frameworks to be able to download the NuGet package in the other projects?
Any ideas for a better approach?
Thanks
Update:
Error #2 The file project.dll didn't exist. It wasn't autogenerated by rebuilding the project. Adding the file manually solved it.
After reading through several answers (.NET Core 3.0 - Preview 2 - Razor views don't automatically recompile on change) that recomend installing Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation NuGet package to get back runtime compilation, I tried the suggested solutions but the project gets the following error message simply by installing the package without even using AddRazorRuntimeCompilation():
The project "project name" must provide a value for configuration
double clicking in the error leads to the following path:
C:\Users....nuget\packages\microsoft.aspnetcore.razor.design\2.2.0\build\netstandard2.0\Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets
But there is no indication as to what is wrong with this file
Add <RazorCompileOnBuild>false</RazorCompileOnBuild> to your .csproj file. That should allow you to build the project.
You also might need <MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish> for publishing to a server.
See example below:
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
...
<RazorCompileOnBuild>false</RazorCompileOnBuild>
<MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
...
</PropertyGroup>
Found this property from this answer on a related question.
Using Visual Studio 2017 (15.3.2)
Create a .NET Framework class library (4.6.2)
Add NuGet Microsoft.EntityFrameworkCore 2.0
You get invalid references to System.Reflection and others.
I can compile, however, in more complicated scenarios when I'm using some functionality of Entity Framework, I am getting run-time exceptions of missing standard System.* libs.
I tried adding the NetStandard.Library first then adding the Entity Framework Core 2 after, but I got the same problem.
I have to use a .NET Framework (Class Lib) as this is a unit test project that is referencing ASP.NETCore2/NETFramework website.
Any clue of what I should be doing?
This can be fixed by letting MSBuild autogenerate the necessary binding redirects by explicitly setting these two properties inside the csproj file (You can put the <ItemGroup> as a child element below the root <Project> element or add to an exiting <ItemGroup> without a Condition= attribute):
<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>
Note that this may issue warnings if you already have an App.config containing binding redirects. you can remove these redirects.
I just installed the newly released Visual Studio 2017 Enterprise (RC). I'm having trouble getting it to work with Code Contracts, however. I have no problem using Code Contracts with Visual Studio 2015. Am I missing something?
As others have noted, Microsoft hasn't prioritized Code Contracts and its long-term support remains unclear (though there has been some ongoing discussion about language-level integration via Roslyn).
As of March 11th, 2017, however, community contributor Yaakov has, at least, updated the source code to include the Visual Studio 2017 build targets (thank you!). This version provides support for both static checking during compilation, as well as run-time validation using CCRewrite.
Note: This build does not provide configuration support via the project's properties pane. As such, code contracts will need to be configured by manually adding the appropriate properties to the csproj file. See #crimbo's answer below for a comprehensive list of properties.
Unfortunately, while these updates have been merged into the master code branch, they are neither reflected in Marketplace distribution or the official NuGet Package. As such, you need to download and compile the source code from the repository (which is easy; just use the supplied BuildCC.bat file).
Important: The static analysis for Code Contracts has a hard-coded dependency on .NET 3.5, which is no longer installed by default in either Windows 10 or Visual Studio 2017. As such, you'll want to ensure this "feature" is enabled (or download it separately); otherwise, you'll get a compile-time error.
Alternatively, as of June 15th, 2017—and later updated on February 6th, 2018—contributor Igor Bek has included this update in his NuGet Package, so the simplest approach is to just add CodeContracts.MSBuild to your packages.config via:
Install-Package CodeContracts.MSBuild -Version 1.12.0
Background: Igor Bek first put this package together as a proof-of-concept for the Code Contracts team, and it was later the basis for the official NuGet package (in v1.10.10126.2). Since Microsoft hasn't updated the official NuGet package, his is now the most up-to-date.
Given the current state of support, I wouldn't encourage people to adopt Code Contracts for new projects, but this should provide backward compatibility for developers who have already invested into Code Contracts for existing .NET Framework projects.
At this time of writing there are no contract definitions for VS2017, but you can get around it with the following if using Nuget package DotNet.Contracts:
Navigate to the CodeContracts nuget package directory (DotNet.Contracts.1.10.20606.1\MsBuild)
Copy the v14.0 folder
Rename it to v15.0
Everything should build as expected.
There is currently no version of Code Contracts for .NET which supports Visual Studio 2017. However the issue can be remedied, if you copy the following target file
C:\Program Files (x86)\MSBuild\4.0\Microsoft.Common.Targets\ImportAfter\CodeContractsAfter.targets
to the ImportAfter location of your VS2017 MSBuild:
C:\Program Files (x86)\Microsoft Visual Studio\2017\#YourVS2017Product#\MSBuild\15.0\Microsoft.Common.targets\ImportAfter
Note: Replace #YourVS2017Product# with your VS2017 product name in the above path, e.g. Community.
This will allow you to build with Code Contracts in VS2017, but will not solve the issue of the CC tab not showing in the Project Settings. For that you will still need to switch to VS2015.
The reasons that code contracts don't work in VS 2017 are:
Code contracts MSBuild files aren't Imported in the VS 2017 tree of msbuild files (easy to fix)
Code contracts configuration UI isn't present in VS 2017 project properties (easily fixed by include CodeContracts msbuild properties)
Certainly questions about the future of CodeContracts are valid, but you can implement the following to enable existing projects that use CodeContracts to build in VS 2017:
Add the contents of C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.Targets\ImportAfter\CodeContractsAfter.targets to your csproj files (either directly or indirectly via an import). The most basic approach is to add this to your csproj file:
<PropertyGroup>
<CodeContractsInstallDir Condition="'$(CodeContractsInstallDir)'==''">C:\Program Files (x86)\Microsoft\Contracts\</CodeContractsInstallDir>
</PropertyGroup>
<Import Condition="'$(CodeContractsImported)' != 'true' AND '$(DontImportCodeContracts)' != 'true'" Project="$(CodeContractsInstallDir)MsBuild\v$(VisualStudioVersion)\Microsoft.CodeContracts.targets" />
Note that the first PropertyGroup shouldn't be necessary if CodeContracts is installed, b/c CodeContractsInstallDir should be specified as an environment variable. In that case, you can get away by just adding
<Import Condition="'$(CodeContractsImported)' != 'true' AND '$(DontImportCodeContracts)' != 'true'" Project="$(CodeContractsInstallDir)MsBuild\v$(VisualStudioVersion)\Microsoft.CodeContracts.targets" />
to your *.csproj files.
Specify all the CodeContracts properties in your *.csproj file (directly or indirectly via Import). Eg:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Code Contracts settings -->
<PropertyGroup>
<CodeContractsAssemblyMode>1</CodeContractsAssemblyMode>
<CodeContractsEnableRuntimeChecking>True</CodeContractsEnableRuntimeChecking>
<CodeContractsRuntimeOnlyPublicSurface>False</CodeContractsRuntimeOnlyPublicSurface>
<CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
<CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
<CodeContractsRuntimeSkipQuantifiers>False</CodeContractsRuntimeSkipQuantifiers>
<CodeContractsRunCodeAnalysis>False</CodeContractsRunCodeAnalysis>
<CodeContractsNonNullObligations>False</CodeContractsNonNullObligations>
<CodeContractsBoundsObligations>False</CodeContractsBoundsObligations>
<CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
<CodeContractsEnumObligations>False</CodeContractsEnumObligations>
<CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions>
<CodeContractsInferRequires>False</CodeContractsInferRequires>
<CodeContractsInferEnsures>False</CodeContractsInferEnsures>
<CodeContractsInferObjectInvariants>False</CodeContractsInferObjectInvariants>
<CodeContractsSuggestAssumptions>False</CodeContractsSuggestAssumptions>
<CodeContractsSuggestRequires>True</CodeContractsSuggestRequires>
<CodeContractsSuggestEnsures>False</CodeContractsSuggestEnsures>
<CodeContractsSuggestObjectInvariants>False</CodeContractsSuggestObjectInvariants>
<CodeContractsDisjunctiveRequires>False</CodeContractsDisjunctiveRequires>
<CodeContractsRunInBackground>True</CodeContractsRunInBackground>
<CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
<CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
<CodeContractsEmitXMLDocs>True</CodeContractsEmitXMLDocs>
<CodeContractsCacheAnalysisResults>True</CodeContractsCacheAnalysisResults>
<CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel>
<CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly>
<CodeContractsAnalysisWarningLevel>0</CodeContractsAnalysisWarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<CodeContractsRuntimeCheckingLevel>ReleaseRequires</CodeContractsRuntimeCheckingLevel>
</PropertyGroup>
</Project>
If you have more than a few projects, I recommend putting these in a private nuget package and referencing that nuget package in each of your projects. The code contracts settings (from step 2) can go in your mycompany.codecontracts.props file, and the code contracts targets (from step 1) can go in your mycompany.codecontracts.targets file.
More info on packaging msbuild properties/targets in a nuget package here: https://learn.microsoft.com/en-us/nuget/create-packages/creating-a-package#including-msbuild-props-and-targets-in-a-package
I'm willing to provide an example on GitHub if there's sufficient interest.
I found the approaches suggested here were not straightforward, in particular that it would require the changes on each developer's machines and on the build server as well.
I decided to create my own very simplified version of Contract.Requires() that will need only global replace of the using declaration in caller classes.
using MYCommon.Diagnostics; //System.Diagnostics.Contracts;
When/if System.Diagnostics.Contracts will be available for VS 2017 and .NetStandard, it will be easy to revert to the proper version.
The actual class is:
/// <summary>
/// Contract.Requires(config != null); in VS 2017 not throw ArgumentNullException
/// The class is workaround for https://stackoverflow.com/questions/40767941/does-vs2017-work-with-codecontracts
/// </summary>
public class Contract
{
public static void Requires(bool condition, string message = null)
{
Requires<ArgumentNullException>(condition, message);
}
public static void Requires<TException>(bool condition, string message=null) where TException:Exception , new ()
{
if (!condition)
{
//https://stackoverflow.com/questions/41397/asking-a-generic-method-to-throw-specific-exception-type-on-fail/41450#41450
var e=default(TException);
try
{
message = message ?? "Unexpected Condition"; //TODO consider to pass condition as lambda expression
e = Activator.CreateInstance(typeof(TException), message) as TException;
}
catch (MissingMethodException ex)
{
e = new TException();
}
throw e;
}
}
}
The essential limitation is that typical usage Contract.Requires(param1!=null); doesn't allow me to throw the exception with the name of the parameter, and better usage is a bit longer:
Contract.Requires<ArgumentNullException>(param1!=null, "param1 is null");