51

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?

Jeremy Caney
  • 4,585
  • 13
  • 32
  • 54
user2106007
  • 677
  • 1
  • 7
  • 10
  • Could someone from Microsoft make a comment please? – user2106007 Nov 28 '16 at 02:39
  • Anyone from Microsoft VS team??? Does VS2017 work with CodeContracts at all? – user2106007 Mar 04 '17 at 03:51
  • 3
    Official issue on github ["CodeContracts not working in VS2017" #476](https://github.com/Microsoft/CodeContracts/issues/476) is opened, as well as [Visual Studio 2017 support #451](https://github.com/Microsoft/CodeContracts/issues/451) – Michael Freidgeim Sep 03 '17 at 06:08

5 Answers5

27

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.

Jeremy Caney
  • 4,585
  • 13
  • 32
  • 54
  • 1
    FYI: I've verified that this information remains up-to-date as of this comment. I changed the comment to link to a later update to Igor Bek's NuGet package, which had been released since I originally submitted this answer. – Jeremy Caney Mar 05 '20 at 23:00
13

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.

Igor
  • 55,253
  • 10
  • 80
  • 149
user6706499
  • 131
  • 2
  • Thank you for the response. Looks like I have to go back to the basic...how do you use CodeContracts nuget package in VS2015? Do you need to install additional component (e.g. Contracts.devlab9ts.msi)? – user2106007 Nov 29 '16 at 21:11
  • Without installing Contracts.devlab9ts.msi, I don't see the "Code Contracts" tab in the project's properties. – user2106007 Nov 29 '16 at 21:16
  • user6706499 - how did you get it to work? do you have to install Contracts.devlab9ts.msi? – user2106007 Dec 03 '16 at 15:24
  • I created a test project on my dev machine, with both VS2015 and VS2017 installed. Opening the project in VS2015 I see the "Code Contracts" tab. But opening the project in VS2017 I **DO NOT** see the "Code Contracts" tab. Any workaround? Information? – user2106007 Dec 12 '16 at 18:59
  • You're a hero!! – Brendan Feb 22 '17 at 15:08
  • 1
    Igor - I noticed that you have edited the answer. I'm wondering if you have made it to work with code like **Contract.Requires(condition)**, which requires **ccrewrites**? – user2106007 Mar 18 '17 at 14:24
  • 2
    @user2106007 - See the answer by `@JeremyCaney`, I installed the NuGet package [CodeContracts.MSBuild](https://www.nuget.org/packages/CodeContracts.MSBuild). That, and editing the .csproj file to specify the configuration is all you need to do. – Igor Mar 19 '18 at 11:42
7

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.

Edin
  • 1,436
  • 11
  • 19
  • Thank you Edin. Does that mean all code contracts features will work, if a project is already configured to use it? – user2106007 Mar 24 '17 at 02:24
  • Yes, exactly, but I would suggest to do some tests to make sure that your dlls were rewritten with CC. – Edin Mar 24 '17 at 06:26
  • Thank you Edin. We have decided to move on, given the uncertain future of Microsoft CC, took about a week's time replacing Microsoft CC with a mini-internally-developed CC framework to preserve the CC concepts and most of its functions. However we really miss interface contracts - I believe you know what I mean. – user2106007 Mar 25 '17 at 13:44
  • 1
    Another option is to leverage Post# (which is commercial but not too expensive compared to rolling your own). The syntax is different (leveraging attributes) but has the same capabilities. – Jimmy Zimms Apr 27 '17 at 13:07
  • 1
    To help others find it: [PostSharp](https://marketplace.visualstudio.com/items?itemName=PostSharpTechnologies.PostSharp) – Artyom Aug 04 '17 at 11:55
  • @user2106007: I'm curious if your organization would be willing to open source (or, at least, publish) your internally-developed CC framework? Even beyond saving other developers the week's development time, it strikes me as something that would benefit from collective contributions. I'm also curious how you were able to address postconditions, since those strike me as the hardest part to replicate without major refactoring of calling code (e.g., by placing `Ensures()` at the end of a member). – Jeremy Caney Sep 24 '17 at 19:45
7

The reasons that code contracts don't work in VS 2017 are:

  1. Code contracts MSBuild files aren't Imported in the VS 2017 tree of msbuild files (easy to fix)
  2. 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:

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

  1. 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://docs.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.

crimbo
  • 8,295
  • 6
  • 42
  • 54
  • @crimbo: Thanks for the detailed answer. Putting this in place doesn't seem to _hurt_ anything during the compilation process, but it also doesn't seem to properly execute CCRewrite. As such, at runtime, I get the error: "An assemble (probably "…") must be rewritten using the code contracts binary rewriters (CCRewrite) because it is calling Contract.Requires and the CONTRACTS_FULL symbol is defined." Is this expected? Is there a scenario in Visual Studio 2017 that supports CCRewrite? – Jeremy Caney Sep 24 '17 at 19:22
  • 1
    @crimbo FYI: I worked around my issue above by instead using the newer [CodeContracts.MSBuild](https://www.nuget.org/packages/CodeContracts.MSBuild/) NuGet package, which includes updates to Code Contracts needed for Visual Studio 2017 support (at least in terms of the MSBuild process). With that in place, I am seeing both static code analysis as well as rewriting taking place. That also mitigates the need to modify the build targets in code contracts, or manually import them into the `csproj` file. – Jeremy Caney Sep 25 '17 at 01:50
  • 1
    @JeremyCaney : I did try the CodeContracts.MSBuild NuGet package as well. That essentially handles step 1 above, but doesn't handle step 2 (it didn't for me). ccrewrite didn't run unless the CodeContracts properties were defined. They can be setup in VS 2015 via the project settings UI (which adds them to your .csproj); or they can be added manually or imported from a file. – crimbo Sep 26 '17 at 21:51
  • @crimbo: That's a good clarification; thank you. In my case, I already had those setup in my `csproj` file. But they are definitely necessary to otherwise enable both static checking and runtime validation. – Jeremy Caney Sep 27 '17 at 22:56
1

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");
Michael Freidgeim
  • 21,559
  • 15
  • 127
  • 153
  • 2
    This is an effective strategy if your primary concern is runtime validation for `Requires()`. And it could be extended to support `Assert()`, `Assume()`, `Exists()`, and `ForAll()` without that much effort. Should they be required, however, it's going to be impractical (without major refactoring of callers) to support `Ensures()`, `Invariant()`, `Result()`, or `ValueAtReturn()` using this approach. In other words, this really only works as a shorthand for validating preconditions. – Jeremy Caney Sep 24 '17 at 19:38
  • 1
    Unfortunately, the lack of support of `Ensure` makes this debatable. On the other hand, white `Require` is easy, `Ensure` is extremely difficult. – Wiktor Zychla Jun 03 '20 at 14:40