Code Contracts Part 7 – Contract Reference Assemblies
Blog Sunday, June 27 2010Until now, I've kept things relatively simple as we've explored Microsoft Code Contracts, using single-assembly examples that help us to focus on understanding the basics of preconditions, postconditions and invariants. Now it's time to understand how a real-world application using Code Contracts might be constructed with multiple assemblies.
Complex objects in the real world are rarely made from just one piece of material. Wooden chairs, for example, aren't often hewn in one part from a single tree. The manufacturing processes for that sort of chair would be both wasteful and cost prohibitive. For such a monolithically carved wooden chair, the grain of the material would expose natural weaknesses along one plane forcing you to add heft to the chair to safely support the people who will use it. Adding weight to the chair reduces portability and can detract from its elegance. As it turns out, gluing or nailing a chair together from disparate bits of wood allows the craftsman to meet many potentially at-odds requirements to create a device that is inexpensive, reproducible, portable, beautiful and strong.
Using the grain inherent in wood is an effective way for furniture makers to separate concerns. The legs of a chair must have parallel shearing strength along the grain. The seat and back are more subject to stresses applied against the grain. Software is similar in some respects. We divide solutions into projects, projects into files, files into classes and classes into members. Those divisions typically fall along the natural boundaries that separate the major functions of the application. Of course, we aren't concerned with things like grain in the software domain. But we are very much concerned with reproducibility, testing and re-use. The ability to grok the software is another key driver for modularity. Adhering to the Single Responsibility Principle throughout a large base of source code commonly results in solutions that consist of a dozen or more projects.
In a more modest example, we looked at abstract types and interfaces in Part 5 of this series. I've updated it a bit for this article and snapped an image of the Solution Explorer in Visual Studio that shows how the solution might be organized. There's one project for the Interfaces, another for the interface Implementation and a third for the demonstration application called TestHarness.
It wouldn't have made sense to put the IAccount interface definition into the same source code file with the SimpleAccount implementation. Placing interface and implementation code into the same class or project makes it difficult to reuse the interfaces cleanly. One exception to the rule with respect to code contract interfaces is the placement of the [ContractClass] and [ContractClassFor]-marked types as demonstrated in Part 5 of this series. Since you'll only define the contracts for an interface once, it makes perfect sense to place the type marked with [ContractClass] in the same project and even the same file with its buddy type marked with [ContractClassFor]. I actually prefer nesting the namespace containing each[ContractClassFor]-marked type inside the namespace containing the companion [ContractClass]-marked type, tacking a suffix of .Contracts onto the inner namespace. Then, it's patently clear to anyone with a modest understanding of Code Contracts what's going on in my code.
Placing interfaces into their own project is highly desirable because it makes them reusable. You can share an assembly containing just interfaces without worry. Interfaces are just type definition, right? There's no code in there, per se. Even the contracts in a [ContractClassFor]-marked abstract class, which look like code, aren't real code in the traditional sense. They are metadata that, in the hands of the runtime rewriter could become code depending on a bunch of variables.
In this tiny example, the test application is also separated out. It deserves its own project, too because we wouldn't want to mingle testing or demonstration code with the interfaces and the implementation. In .NET speak, this solution will produce three separate assemblies: two dynamic link libraries (DLL) for the Interfaces and Implementation projects and one executable (EXE) for the TestHarness project. Taking what you know so far about code contracts into consideration, your impulse may be to enable the static and/or runtime checkers for all three projects. That's a good start but when you build a solution configured that way, you'll get some new warnings out of the box.
These three warnings are related to the assembly references in the solution. The Implementation project has a reference to the Interfaces project which yields the first warning labeled as line 2 in the error list. The next two warnings, labeled as lines 3 and 4 are related to the fact that the TestHarness project has references to both the Interfaces and the Implementation projects. What do these warnings mean? And what is this "Contract reference assembly" to which the warnings refer? Before I explain that, let's examine the Code Contracts tab of the project properties for any one of these projects.
An important group on the Code Contracts properties page that we've been able to ignore so far has the heading Contract Reference Assembly. It has two knobs:
- A dropdown list for specifying (none), Build or DoNotBuild. The choice determines whether or not this contract reference assembly thing, whatever it is, gets built or not.
- A checkbox determining whether to "Emit contracts into XML doc file" or not. We'll focus on how code contracts can augment your project documentation in a future article.
The default choice in the dropdown list is (none). In earlier builds of Code Contracts, the default was Build which causes a special, separate assembly to be built that contains the contracts for the assembly produced by the project. For an emitted assembly called Jabberwocky.dll, for example, selecting the Build option under Contract Reference Assembly will cause a second assembly called Jabberwocky.Contracts.dll to be emitted. This contract reference assembly, as it's called, will contain classes named and shaped just like the ones in the Jabberwocky assembly. However, rather than containing code, the classes in the contract reference assembly will contain just the contracts.
Sometime before release to manufacturing, I suppose the Code Contracts team at Microsoft decided that it was a bit presumptuous to automatically produce a contract reference assembly when Code Contracts is installed. The choice to build or not build can be made irrespective of the choice to use the static and runtime tooling and building a contract reference assembly can slow down the build process a bit. Other than that, there's little risk in building a contract reference assembly.
Whatever Microsoft's motivation was, the (none) option was made the default. The (none) choice is effectively the same as DoNotBuild in that no contract reference assembly will be written. But it's also a signal to tooling that you haven't made a choice. The warnings shown above are specifically designed to evoke a response from you. Do you want to build a contract reference assembly or not?
By selecting (none) or DoNotBuild, the contracts from the Interfaces and Implementation assemblies will be cut off from their consumers. An easy way to prove this is to modify the Program.cs file to comment out the test of the Balance property on a SimpleAccount object before making a withdrawal from it:
//if (account.Balance >= 50.0d)
account.Withdraw(50.0d);
With the static checker enabled, we would expect a warning about an unproven requirement from this code, due to the pre-condition that was placed on the Withdraw() member of the IAccount interface. It demands the Balance amount be sufficient to support the withdrawal so failing to check it first should emit a warning. But the contracts on the IAccount interface have been cut off from the Implementation assembly and the TestHarness assembly by not making a choice, i.e. using the default (none) option for Contract Reference Assembly generation.
OK, this is an important inflection point in the learning process. You need to understand which contract reference assembly choices should be made. There are three assemblies in this solution. Which of these three need to have a contract reference assembly generated? The TestHarness assembly depends on Implementation assembly which in turn depends on the Interfaces assembly. But the TestHarness also has a direct dependency on the Interfaces assembly, too. This latter requirement is enforced by the build engine since there is an indirect dependency through the Implementation assembly. The references in the solution can be visualized like this:
At this point, we have three warnings about the failure to make choices and a missing warning about some bad code we used to test our assumptions. We could get rid of the warnings we don't want by simply changing the Contract Reference Assembly generation options in the Interfaces and Implementation projects from (none) to Build. But do we really need to build both? Think about it for a moment. Where are the contracts defined for the IAccount interface? Are they in the SimpleAccount implementation? No, they are technically in the buddy class inside the Interfaces project.
Remember: this is the one time that I think it's OK to put what appears to be implementation code in an interfaces-only assembly. Contracts aren't code in the traditional sense. They are metadata so it's A-OK to include them alongside the interfaces themselves. Doing so will simplify the build as you'll see below.
Logically, with this arrangement, you should be able to change the Contract Reference Assembly generation option on the Interfaces project to Build and the same option for the Implementation project to DoNotBuild to correct both kinds of problems. The warnings about not having made Contract Reference Assembly generation choices should disappear and the warning we want to see concerning the failure to prove the precondition about the Balance being greater than or equal to the size of the withdrawal amount should reappear. Making those changes, we now see:
Nice. We could have selected the Build option for the Implementation project, too but it wasn't strictly required because there aren't any new contracts introduced there. Selecting DoNotBuild on the Implementation project will keep the build process quick and let the Code Contracts tooling know that we've made an informed choice. If you add contracts to the Implementation classes in the future, specifically to methods not implementing interface methods, you'll probably want to change your choice. But for now, enabling the generation of a contract reference assembly for the interfaces-only assembly is adequate.
I suppose it goes without saying that the TestHarness assembly which is an executable (EXE) isn't likely to be referenced by other projects. So, of course, the default choice of (none) for Contract Reference Assembly generation is adequate there. Personally, I prefer to make a discrete choice concerning this option for every project, even those where the default generates no warnings. For the TestHarness project, I would change the option to DoNotBuild to be explicit.
In closing, let me go back and fix up a slight untruth from earlier. Well, it's not a lie but there's a wrinkle here that you should know about. I said before that the choice to use the static and runtime checkers was independent of your choice to produce a contract reference assembly or not. And that's true from the perspective of building any single assembly. But across projects, if a dependent assembly uses the static or runtime checkers, the dependee projects should make choices concerning contract reference assembly generation: Build or DoNotBuild.
That makes my head swim, too so allow me express it in terms of the example we've been looking at. If you didn't use the static or runtime checkers in the TestHarness application, the Interfaces and Implementation projects wouldn't need to generate contract reference assemblies. The default choices of (none) and (none) for those two dependee projects would be OK, too. No warnings would be generated by the static checker, for example. But as soon as the static checker or the runtime checker is enabled on the TestHarness project, contract reference assembly choices would be required for both of the dependee projects. Warnings would be emitted as soon as the static checker or the runtime checker were enabled on the TestHarness project. The brilliance of Microsoft's inclusion and default selection of the (none) choice becomes apparent now. The build is both fast and quiet until you decide to begin using Code Contracts.
That's all for now. Next time, we're in for a treat. An interview witht the Microsoft Code Contracts team where they'll answer some of your questions. Enjoy!
This blog post is part of a series concerning Microsoft Code Contracts. For a complete series directory, please refer to the following list. Articles without a link will be published as they become available.
- Part 01 - Introduction
- Part 02 - Preconditions
- Part 03 - Postconditions
- Part 04 - Object Invariants
- Part 05 - Abstract Types and Interfaces
- Part 06 - Runtime Support
- Part 07 - Contract Reference Assemblies
- Part 08 - An Interview with the Microsoft Code Contracts Team
- Part 09 - Contract and Invariant Inheritance
- Part 10 - Pex Integration
- Part 11 - Documenting Your Code Contracts
- Part 12 - The Command Line Tools
- Part 13 - Contracts in Continuous Integration
- Part 14 - Writing Custom Runtime Helpers and Rewriters


7.04.2010 at 6:57 AM
From my limited point of view, Microsoft's choice of (none) and DoNotBuild seems pretty stupid: Why not use DoNotBuild as default, and then issue warnings when any project actually needs it?
Also, having two almost equal values will only work when you understand the reasoning behind them. That understanding does not come from staring at the options in the Contracts tab.
I didn't understand those two values until this blog post arrived. And I haven't seen anyone else trying to explain them, either. Now that I think I understand what they are meant for, I can't help myself thinking: WTF!?
7.06.2010 at 7:35 AM
Hello Thomas. Yes, it is confusing. But I do believe there's logic in the choices that Microsoft made. It's the packaging that could use some work. In particular, the nuances between the (none) and DoNotBuild choices requires too much foreknowledge I believe. I've been thinking through the alternatives for packaging the choices and I have a few ideas. I am going to post them on the MSDN Forum for Code Contracts when I think I have them refined well enough. Be sure to go there and give them some feedback, OK? They really do listen and take note of your opinions. You have the chance to shape the product at social.msdn.microsoft.com/.../threads