How CODY Systems Succeeds with PostSharp

by Britt King on 18 Sep 2012

This is a guest post by Alessandro Federici. Alessandro is a System Architect at CODY Systems, an industry leader in the collection, analysis, and sharing of critical data for public safety, law enforcement, and intelligence agencies in the United States for over 30 years. CODY Systems specializes in integrated public safety software solutions and interoperable vendor-neutral information-sharing for First Preventers.

Occasionally we’re asked by teams that are evaluating PostSharp to provide examples of organizations using it in real-world scenarios. I understand the request completely. It’s one thing for teams to try a new product that’s backed by loads of documentation and tutorials, but learning how companies from a variety of different industries are using it to successfully produce cleaner, more efficient, code builds trust in a way no other document can.

In the past, we’ve pointed to success stories such as FullArmor, Siemens Audiology, Thales Information Systems and IdeaBlade. In today’s post, Alessandro shows how he and his team at CODY are using PostSharp in a variety of ways to reduce code clutter, handle errors, simplify what they write and make their code more readable.

Enter Alessandro

My interest in Aspect Oriented Programming (AOP) started in 2005, approximately. At the time I was working at RemObjects Software (I was one of the two founders) and we had recently started the development of our compiler. The engineers behind it planned to natively support AOP. I didn't know anything about aspects at the time, but the moment I saw and had a chance to use them I got hooked and I realized their elegance and power. In a way, I think I had the classic "how could I live without them?" moment where you lean back on your chair, stair at the monitor and question decades of your (programming) life.

Years then passed. I left RemObjects Software and I am now System Architect for CODY Systems where we develop applications using exclusively C#. As a Delphi/Oxygene old-timer, I had my moments with C# but I came to love it. Yet, the lack of native AOP features has always bothered me. To use a parallel example, adopting aspects feels like adopting lambda expressions and Where() calls: Once you start using them there's no going back to for-i loops that embed if statements. It gets you to the same end-result, but it just feels and reads sloppy, to me.

A client-tier rid of unnecessary code

CODY is currently working on major enhancements of the infrastructure and picked some of the best-of-breed tools for .NET. PostSharp is definitely one of them and will play a large role in our re-factoring and re-engineering efforts.

Our system is built off of different modules and follows some of the common multi-tier design patterns. We have a client tier, an application-server tier and a data-storage tier. The client-tier is made off of different client applications: Some are written using WPF, one is in Silverlight and others use MVC, depending on the need.

Our WPF and Silverlight clients are all built using the MVVM pattern where the INotifyPropertyChanged interface plays a fundamental role. This is the first place where PostSharp will greatly help us re-factoring leaner and cleaner code. Currently we have hundreds of properties that explicitly fire notification events with code such as this:

public string FirstName
{
    get { return _firstName; }
    set { ChangeAndNotify(value, ref _firstName, () => this.FirstName); }
}

We have put effort into alleviating the most common problem with INotifyPropertyChanged: We don't use error-prone, "magic" strings to reference property names and we have code snippets that help writing that code. Still, in the end, we continue to have hundreds of lines of code that have nothing to do with business logic and only exist to address what is, in most cases, just a user interface concern.

Thanks to PostSharp, all that baggage will go away and we will be able to use automatic properties where possible and concentrate on the business logic associated to the property.

The code above can now turn into the following:

[NotifyAspect]
[CapitalizeNameAspect]
[StringLength(1, 100)]
public string FirstName { get; set; }

 

This new code (which is also one line shorter) does everything the previous one was doing but also adds two new extra “business” operations. It's more readable and directly communicates the intent behind FirstName.

I also compared the amount of bytes the old code and new code took on disk: The new code is less than half of what it used to be. I could also give the new code to non-developers who would have a chance to understand what they got, finally!

But there's more we can do, thanks to the fact PostSharp aspects are implemented through attributes.

Self-documenting validation aspects for a discoverable and rock-solid server-tier

One of the modules of our system generates HTML documentation by using reflection. Any time we add a new domain object, new documentation pages get dynamically generated and provide an English description of all its properties, along with their constraints and other useful information. The data annotation StringLength displays as “The length of FirstName must be between 1 and 100”. CapitalizeAspect displays as “The value assigned to this property will be automatically capitalized”. NotifyAspect only displays when it's not attached to a property, as a reminder of a special case.

In other words, aspects allowed us to first add and later infer behavior through the use of simple attributes. All that without having to maintain two separate documents containing such information: the code and manual text just for help files.

This feature is critical for our application server tier. CODY's application server has been openly accessible for years, through simple HTTP calls and XML messages. It's a very important part of our infrastructure for many different reasons. The features we are now adding make this tier much easier to understand, use and test and the self-documenting aspect is of paramount importance in all this.

This brings us to the next use of PostSharp: ServiceMessageHandlerAspect. Our API layer is composed of services whose methods receive one request and return one or more responses. For instance, our sample CalculatorService is coded as:

[Description("Performs simple mathematical calculations")]
public sealed class CalculatorService
{
    [...]
   
    [ServiceMessageHandlerAspect]
    [Description("Sums two integer numbers")]
    public SumResponse Sum(SumRequest request)
    {         
        return new SumResponse()
        {
            Result = request.A + request.B
        };            
    }        
}

SumRequest is coded as:

[Description("A message that contains two operands (A and B) for a sum operation.")]
public class SumRequest : ServiceRequest
{
    [...]

    [Required]
    [Range(0, 1000000)]
    [Description("Number A")]
    public int? A { get; set; }

    [Required]
    [Range(0, 1000000)]
    [Description("Number B")]
    public int? B { get; set; }
}

 

The implementation of CalculatorService.Sum() only returns a result with the sum of A and B, but ServiceMessageHandlerAspect does a lot more for us, behind the scenes. When this aspect is applied to a method, validation code ensures that all the data annotations bound to SumRequest are satisfied (e.g. A and B are not null and their values are between 0 and 100000) before letting the flow get into the Sum() implementation. If any requirements are not met, the execution of Sum() is skipped and a SumResponse containing error information is returned to the caller.

In other words, by using this aspect and by defining solid request classes with validation attributes, we can be sure that whenever our service methods are called, their request argument is well formed and contains everything that is needed for it to be processed.

Among the same lines, responses can also contain validation attributes. ServiceMessageHandlerAspect ensures that whatever output is generated by the method matches such requirements.

Uniform and standardized access to 3rd party products through exception wrapping

Finally, we have another aspect that greatly helps us in error management: The WrapExceptionsAspect.

Like I mentioned before, we picked a number of the best-of-breed tools for the development of our core. We have queuing and caching integrated in the core, for scalability purposes. While these products are very solid and unlikely to disappear from the market, we abstract them and access them only through interfaces. What we cannot abstract easily are the errors these products return. Some might have their own exception types while others might use standard .NET Exception classes. What we wanted, in our wrappers, is to be sure that whenever an error occurs in the current cache implementation, we always get back a CacheException (which then embeds the original exception).

The following code shows how we use the WrapExceptionsAspect:

public abstract class BaseCache : BaseDisposable, ICache
{
   [..] 
    [WrapExceptionsAspect(typeof(CacheException), null)]
    void ICache.Store(string key, object value, int? maxMinutes)
    {
        if (!InternalStore(key, value, maxMinutes))
            throw new CacheException(string.Format("Cannot store item [{0}] in the cache", key));            
    }
   [..] 
    [WrapExceptionsAspect(typeof(CacheException), null)]
    bool ICache.TryGet(string key, out T value, int? maxMinutes)
    {
        return InternalTryGet(key, out value, maxMinutes);            
    }
    [..] 
}

 

The above could have been easily accomplished by using a try...catch block around the calls to the Internalxxx methods but that would have meant having that extra code in each of the methods above and in any of the other wrapper classes we have (e.g. Queuing, etc.).

If we were to change how exception interception worked we would have had to touch every method. With the aspect we built we can be sure that there's one place to touch and all our wrappers immediately change.

Conclusion

Any piece of code that performs a function can be written in infinite ways. Many of such implementations will work and will produce the same results, but I strongly believe the result is not all that matters. How you achieve your goal is, many times, just as important. If a certain implementation is hard to read, understand or modify, it will cause problems down the road. Using aspects helps reducing clutter in code. It helps simplifying what we write and, later, what we read. It's a very elegant, simple and effective way to write code that focuses on the real problem, not language or framework needs.