As I approach the end of my current contract it appears less and less likley that funding will be available for my renewal. A shame really considering the progress we have made on the new version of the application - which has manifested into a pretty damn nice codebase, even if I do say so myself. But unfortunately administrivia is once again about to become the downfall of a potentially promising project.
So with that news I announce myself available. I am a little shaken by this news but I am positive that some good will come of it. If anyone knows of any suitable local or telecommutable work - please keep me in my mind.
I have uploaded my current CV to my about page.
For those who care to play along - I am now twittering. I don't do it that often, but when I do - pure gold baby!
Not really.
My username is sburman. Check me out here - http://twitter.com/sburman
The refactoring of the helper method Html.Form in MVC to Html.BeginForm was published in the release notes as a simple renaming. During the beta upgrade process we noticed that a lot of our forms were rendering with blank actions.
If you too are having trouble with your forms, it is worth noting that the API has changed in a subtle but EXTREMELY significant way. The orignal method, whose signature looked like:
public static IDisposable Form(this HtmlHelper helper, string controllerName, string actionName)
Has been changed to read:
public static MvcForm BeginForm(this HtmlHelper htmlHelper, string actionName, string controllerName)
Note that the parameters have been interchanged! So if you were using magic string-ness to get actions into your forms, you now have a bigger upgrade job than you thought. You will need to switch the parameter strings and put your controller where your action is - and vice versa.
This was tracked down in my team by Simon Sanderson (who we are assured is unrelated to MVC guru Steve). There is a confession of sorts from the MVC team here.
In the past few weeks I have revisited the specification pattern using Linq. I have teased it, toyed with it and tricked it up. And today I announce the public availability of a project demonstrating my current thoughts. You can grab the solution from Google Code at http://code.google.com/p/linq-specifications.
I have no doubt that improvements can and will be made if this generates any wider adoption. For now, it suits what I am currently working on quite nicely. YMMV.
Below is a class diagram of the current core of the project. Use it as a quick reference but be sure to check out the code for a more in-depth view.
Shortly I will do up a few examples on the project wiki that will flesh out some of the gotchas I have come across already and I need some more testing around some of the elements. For now, however, I just wanted to get this out in the wild. Feel free to comment/flame. I look forward to any feedback.
Using the container scoping capablity of Autofac makes NHibernate session management a breeze. Obviously this only handles the most basic session-per-request scenario but we know that can cover a large percentage of most peoples requirements anyway. I am sure other containers could get to this functionality as well, but I was blown away by how easy it was aith Autofac. Below is my session management configuration:
builder.Register<NHibernateInstance>().SingletonScoped();
builder.Register(c => c.Resolve<NHibernateInstance>().CreateSession()).As<ISession>().ContainerScoped();
builder.RegisterGeneric(typeof (Repository<>)).As(typeof (IRepository<>)).ContainerScoped();
So the key points are:
- Plug in the Autofac HttpHandler for ASP.Net integration by following the instructions here
- Have a singleton instance of the NHibernate session factory (housed in my NHibernateInstance class)
- Have a method on that singleton registered instance that returns an ISession (CreateSession in my example)
- Configure a container scoped instance of the NHibernate ISession, thus ensuring that the created instance will not cross request boundaries
- Have the container dynamically inject the ISession into your IRepository implementation
For more information on container scoping in Autofac for ASP.Net web applications check out the Autofac wiki article.
In the previous to parts to this series (here and here) I showed the basic infrastructure framework for using Linq-based specifications with a flexible repository interface. In this article I dig deeper into the Expression based find functionality and it's implementation.
The Goal
Specification based query support on the repository is a great feature for encapsulation of any query logic. My major problem with this approach was the creation of trivial specifications on a per-domain-object basis. This would litter the code with specifications that had very little functionality contained within them. Generally this is not a bad thing but, in this instance, for the sake of maintainabilty and simplicity I wanted a to be able to perform ad-hoc queries.
The solution was to make my repository able to accept queries like :
var repository = new Repository<Person>(session);
var people = repository
.FindAll(p => p.Name == "steve");
The AdHoc specification
Since I have already made the decision that my MatchingCriteria are of type Expression, the AdHoc specification is trivial - but very cool. The implementation looks like :
public class AdHoc<T> : Specification<T> where T : IEntity
{
private readonly Expression<Func<T, bool>> expression;
public AdHoc(Expression<Func<T, bool>> expression)
{
this.expression = expression;
}
public override Expression<Func<T, bool>> MatchingCriteria
{
get { return expression; }
}
}
Simplifying the repository using the AdHoc specification
By utilising this simple but powerful specification class we can simplify the api to our IRepository. We are now able to add a FindOne and FindAll overload that accept Linq based queries in an AdHoc fashion. The implementation is as simple as :
public IQueryable<T> FindAll(Expression<Func<T, bool>> expression)
{
return FindAll(new AdHoc<T>(expression));
}
This functionality is able to be consumed in the following fashion (satisfying our goal for this task) :
var people = repository.FindAll(p => p.Name == "steve");
Conclusion
In combination with the flexibility built into the IRepository interface we now have a basis for a data access layer with maximum flexibility. Sql queries are created at execution time to reflect the criteria that we have applied through domain specifications - be they concrete or ad-hoc.