I was a big fan of CSLA when the .NET v1.1 version of it came out, and used it to some great success on a project back then. I've been following its progress since then and although it certainly has some cool concepts in it, I feel as though it is slowly but steadily succumbing to the problem that is the eventual death of all frameworks, the "silver bullet syndrome". CSLA has increasingly been trying to become everything to everybody and in the process I think it is becoming overly complex and heavy-handed as all frameworks that are around long enough seem to do. He's got some great concepts that I will consider borrowing for our implementations, but the framework as a whole no longer matches my style, nor indeed it seems the community at large.
So back to the original point of this post, as we evolve to a more DDD oriented model than a CSLA oriented one, one of the main challenges we are working on is what the right way to do validation is. I agree with Oren and Udi that there are different types of validation and that they should be addressed in different ways. The first piece of the puzzle I attempted to tackle is what they termed "business rule validation". To me it makes sense that any kind of complex (non-input) validation should be performed by a specific object dedicated to that task as it promotes the greatest flexibility and reuse. This got me to thinking about the specification pattern as this type of validation is just one of the cases that pattern can be used for. The other great use of this pattern is for selecting a subset of objects from a master list, which ties in very well with the repository pattern for object retrieval. This line of thinking eventually lead me to the idea of somehow creating an implementation of the specification pattern that could be used for both validation and entity selection (using LINQ) types of scenarios. It seemed like a reasonable goal and one that could be very beneficial if done correctly.
As I am a #pragma-tech programmer (pragmatic, get it?), I always like to see what work others may have done in the area. I hate being a plumber so I fired up a google search and turned up several articles on implementations of the specification pattern in .NET but the ones that were most interesting to me were this one and this one, both by Ian Cooper. I'll spare you the gory details of how its all works, you can go read Ian's articles for that. In short though, in these articles Ian pretty much lays out an implementation for exactly what I wanted to accomplish, but unfortunately neglected to include a download for the code. Luckily his articles themselves were detailed enough and contained enough code that I was able to get the gist.
As I wanted to spend some time honing my C# / LINQ / lambda skills anyways I decided to take a crack at fleshing out an implementation of what he talks about to see how it all comes together. A few hours later I had it all banged out and the default implementation of what he was talking about worked quite nicely. There were a few warts with it that I didn't much like however that I decided to fix in my implementation...
The first was that the only way to chain specifications was by writing code like this:
Specification ProductSpec = new Specification
I didn't like the fact that you had to create a new specification object inside the chaining method to make this work, so I overloaded those functions to also just directly take a lambda expression so you now you can do this which is more fluent in my opinion...
I also added extension methods for the IQueryable
var prd =
(from p in products
select p).WhereSpecification(ProductSpecifications.ProductIsConfigurable);
I also added caching of the compiled expression to improve performance.
The end result is a way of creating a single object using a pretty fluent interface that you can then use to validate an object's state, or use in a LINQ expression against any type of LINQ datasource to specify a filter criteria. Pretty cool!
The bits are here...
Hi, the link is broken
ReplyDeleteUpdated the link in the post so that it works now.
ReplyDelete