Introducing Advanced Code Contracts with the Entity Framework and Pex (Cont.) Advanced Concepts Now I’ll introduce advanced programming techniques. You don’t have to master these concepts in order to use Code Contracts, but if you want to take them to another level and improve your software development skills, don’t overlook this section. Object Validation As a supplier, the Customer entity now implements contracts for its properties and any client will benefits from and be forced to abide by the rules you have set. Now what? Should you be content with that? Not yet. How can a client of the Customer entity knows that it can safely start using an instance of the class? All you have provided are safeguards against misuses of the entity properties. Because an ADO.NET entity must supply a parameterless constructor, nothing prevents an instance of Customer to be created and passed around without having its properties properly initialized. Sure, a contract violation will be detected at some point, but not necessarily at the best of times. Now that you have contracts in place, however, you can do better: you can provide an implicit overall contract stating that a Customer instance is valid as a whole. The idea is not a new one. Again, I bring an Enterprise Library concept to Code Contracts. The Validation Application Block comes with an ObjectValidator whose purpose is to validate the instance of a class based on validation attributes applied to its properties or fields. If all specified validations are satisfied, an object is said to be valid. Now, you don’t need the VAB anymore, you’ve got contracts! You could think first of using invariants to make sure that an object is in a valid state, but since invariants apply at all times, even on parameterless constructors, they’re not the best fit for a solution. Also, they don’t apply on property getters. Furthermore, you wouldn’t want to specify invariants to validate all Customer properties, since the properties themselves already provide their contracts. To introduce in Code Contracts the concept of object validation in an Enterprise Library-like fashion, you have to agree to this principle: If all the properties of an object have valid contracts, then the instance itself is considered in a valid state. To enforce that, you simply have to invoke all the getters to trigger the validation of their contract clauses. I provide the object validation feature for Code Contracts with the extension method IsValid, which you can use to validate an instance anywhere in your code, including as part of a contract clause. The IsValid method works with the ContractValidation attribute, which you can apply to a class, interface, or property. When you use the ContractValidation attribute on a class or interface, the IsValid method verifies all the properties of that class or interface. When not set to such a level, you can apply the ContractValidation attribute to specific properties to limit validation to those properties. Furthermore, the IsValid method also processes non-public properties. Thus, to make the Customer entity support object validation, you just add the ContractValidation attribute to the ICustomer interface of Listing 5: [ContractValidation] [ContractClass(typeof(ICustomerContract))] public interface ICustomer
Now, whenever a client invokes the IsValid method on a Customer instance, each contract clause in the getters of the ICustomer entity properties will be checked. The IsValid method will return true only if all contracts are verified successfully: Contract.Requires(customer.IsValid());
When a contract clause using the IsValid method fails, you don’t get the information about which property is in error, but you can take advantage of the debugger and explore your object properties to find out, as shown in Figure 5.  Figure 5: Exploring customer properties and contract failures while debugging.As an alternative, you can call the method ContractValidationAttribute.Validate, which coldly throws a ContractValidationException if a contract clause is violated. This exception exposes a PropertyInfo instance describing the property that failed. As you can presume, the IsValid method indirectly uses reflection and its evaluation can be a costly operation. Choose wisely the places in your code where you invoke it. Note also that the validations work only if you’ve set the contract run-time behavior to throw exceptions on contract failures. You’ll find the IsValid method, the ContractValidation attribute and ContractValidationException as part of the Code Contracts Extensions project on CodePlex. Self Validation The Enterprise Library Validation Application Block also has the concept of self validating objects, whereas one class can supply a method whose role is to validate an instance state. Although the Code Contracts Extensions don’t provide anything specific for that feature, you can implement self validation by extending the object validation technique presented in the previous section and using a dedicated property to do your validations: [ContractValidation] private bool SelfValidates { get { Contract.Requires( !this._FirstName.IsNullOrEmpty()); Contract.Requires( !this._LastName.IsNullOrEmpty());
return true; } }
Only apply the ContractValidation attribute to your property if you want self validation to be evaluated in the IsValid method calls alongside other object validations. Otherwise, invoke the self validation property directly. Lazy Initialization Sometimes, having strong contracts can become a burden. When you define very strict clauses in an entity, you may trigger exceptions when you don’t want to. Let’s say you have the following contract for the Address entity, which ensures it’s never null and is within a given length: string IAddress.CountryRegion { get { Contract.Ensures( !Contract.Result<string>().IsNull() && Contract.Result<string>().Length.IsInRange( 1, 50)); return default(string); } // Setter skipped }
Then, assume that you’re getting an Address instance from a simple factory (which doesn’t set any property value): public class AddressFactory { public static Address GetNewAddress() { Contract.Ensures( !Contract.Result<Address>().IsNull()); return new Address(); } }
You now need to initialize the Address properties, but may have to perform some logic on them to do that. In the code snippet below, I check the value of the CountryRegion property before initializing it, but because its getter contract states that its return value can’t be null-and that its value is effectively null (not initialized by the factory), I’ll get a contract exception when calling the getter: Address address = AddressFactory.GetNewAddress(); if (address.CountryRegion == null) // Throws address.CountryRegion = GetCountry(address);
This is a situation where you perform lazy initialization (or delayed construction) of your object. This scenario occurs frequently when you have a default parameterless constructor or when proper initialization is not enforced by a constructor. This is the case here with an ADO.NET entity, but the same issue could also arise with an Inversion of Control Container or Data Transfer Objects exposed by Web Services, among others. To fix the problem, you need some kind of lazy contract evaluation. You want the CountryRegion getter contract to be effective, but not while you’re initializing your Address entity. The solution comes in two parts: managing the initialization state and defining better contracts. In order to manage the initialization, you can use the IInitializable interface. This interface defines a single property, IsInitialized. The contract for the IsInitialized property stipulates that once you set the property to true, the implementing object must have a valid state. So, it makes sure that you can’t set an object as initialized if it’s not properly constructed. Listing 8 presents IInitializable and its contract. Naturally, since the object validation technique is used here with a call to the IsValid method, the implementing class must support it. Now that you have the IInitializable interface, you can use its IsInitialized property value in other contracts to implement laziness. If you go back to the CountryRegion getter presented earlier, you can rewrite it like this: string IAddress.CountryRegion { get { Contract.Ensures( // Laziness (this is IInitializable && !((IInitializable)this).IsInitialized) || // Contract (!Contract.Result<string>().IsNull() && Contract.Result<string>().Length. IsInRange(1, 50)));
return default(string); } // Setter skipped }
The first part of the statement checks if the Address object is initialized; if not, it doesn’t enforce the contract clauses. However, if the Address is set as initialized, the contract kicks-in. Invariants can also benefit from laziness; they just have to check the IsInitialized property before deciding whether to apply their contract. Invariants are automatically validated when you set the value of the IsInitialized property to true, because they are injected in the setters by the Contract Rewriter. Listing 9 shows the Address entity with full lazy contract evaluation. You can now perform lazy initialization without a problem: Address address = AddressFactory.GetNewAddress(); if (address.CountryRegion == null) // Safe address.CountryRegion = GetCountry(address); // Other initiatizations skipped... address.IsInitialized = true; // Auto-validated
You may have noticed earlier on that the Ensures clause in the new CountryRegion getter contract verifies with the is operator that the current instance (this) implements IInitializable. This is a very powerful condition to add to your lazy contracts. IAddress and IInitializable are two distinct interfaces; you have no guarantee whatsoever that an Address class would implement both. By adding the is statement check, laziness is only enabled in the postcondition if the implementing class also implements IInitializable. Otherwise, the contract clause behaves exactly like the strong one introduced at the beginning of this section. If you download the companion code of this article, see how the Address contract behavior changes just by removing IInitializable from the list of interfaces it implements! Note that IInitializable and its contract are also among the things you’ll get from the Code Contracts Extensions project on CodePlex. Error Handling The importance of an adequate error handling mechanism in a software system is often underestimated. The Code Contracts team has worked hard to provide you with many interception points to manage contract violations. The first thing to understand is that, while Code Contracts supports exceptions, the overall design of the run-time error handling subsystem is not necessarily exception-based. It instead allows any type of error management you deem appropriate. Another thing to note about throwing exceptions is that while Requires<> gives you the ability to throw a specific exception, it only allows you to specify a string message for it. If your exception needs additional arguments, you must resort to what is called legacy requires, which use the old if/throw pattern. Such code behaves similarly to Requires<>, but provides better exception support: void SampleLegacyRequires(Customer customer) { if (!customer.IsValid()) throw new CustomerException(customer); Contract.EndContractBlock();
// Implementation skipped... }
While that feature is available to emulate Requires, it isn’t available for Ensures and the other contract clauses. You can also alter the default run-time contract behavior by defining a class implementing specific handlers. Listing 10 presents the CustomContractsRuntime class, which I created to illustrate error handling customization. CustomContractsRuntime mostly simulates the default run-time contract behavior. Notice the TriggerFailure method: this is where you specify how errors should be handled. Remember the code injection of the Contract Rewriter introduced in Listing 3? Take a good look at it again and you’ll see that it involves __ContractsRuntime and CustomContractsRuntime calls. This is because the Contract Rewriter injected calls to my custom run-time contract behavior class. I enabled this by configuring the Custom Rewriter Methods in the Code Contracts property panel (Figure 1). For the Assembly I specified CodeContractsExceptions and for the Class I specified CoDeMagazine.CodeContracts.CustomContractsRuntime. Be aware that you must specify the run-time contract behavior in all the projects you want to customize: keep in mind that this alters the way the target assembly is rewritten and that these specialized methods are not global handlers. In the companion solution, I configured the custom behavior only for the Client application; the other projects still throw standard ContractException exceptions. When I first experimented with run-time contract behavior, I was a bit puzzled by the fact that there are differences in the methods called by the contracts clauses. To help you demystify their workflow, I present it in Figure 6.  Figure 6: Run-time contract behavior.Run-time contract checking also provides a global event, Contract.ContractFailed, which is raised when contracts are violated. That event is system-wide and works across assemblies. This is the preferred way of intercepting contract clause errors in your application: Contract.ContractFailed += (sender, e) => Console.WriteLine(e.Message);
The ContractFailed event argument is ContractFailedEventArgs. You can use it in your handler to mark the error as handled and continue with the normal execution path by calling the SetHandled method, or to force the triggering of the failure after all handlers have executed by invoking the SetUnwind method. Although ContractFailedEventArgs contains information about the state and nature of the current error, it doesn’t provide you with the exceptions raised by a legacy requires or Requires<>, because those exceptions aren’t thrown yet when the ContractFailed event fires. |