- Posted by liammclennan on October 13, 2009
If you are using fluent nhibernate then mapping nullable columns is easy – just call the .Nullable() method for the property mapping:
Map(x => x.Name).Nullable();
Unfortunately, my mind has been poisoned by Rails and its convention over configuration value, so I can't ignore automapping. Automapping uses configurable sensible defaults to do the mapping for you. The out-of-the-box defaults make every property nullable, which does not suit my style, so I wrote an IPropertyConvention to override this behaviour and make everything not-nullable:
public class NotNullPropertyConvention : IPropertyConvention
{
public bool Accept(IProperty target)
{
return true;
}
public void Apply(IProperty target)
{
target.Not.Nullable();
}
}
My plan was to use fluent nhibernate’s Nullable() method to override this default for properties that I actually wanted to be nullable. But it doesn’t work. The automapping convention overrides the fluent nhibernate setting. To get things to work the required way I had to modify the NotNullPropertyConvention to:
public class NotNullPropertyConvention : IPropertyConvention
{
public bool Accept(IProperty target)
{
return !IsNullableProperty(target);
}
public void Apply(IProperty target)
{
target.Not.Nullable();
}
private bool IsNullableProperty(IProperty target)
{
var type = target.PropertyType;
return type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof (Nullable<>));
}
}
This updated convention is only applied to properties that are not nullable. Perfect!
- Posted by liammclennan on September 17, 2009
I continue to be interested in the architecture of the plain, standard, web application. Each of the web application frameworks defines an architecture for the plain web application. Think of Rails and Django (MVC + Active Record), or sharp architecture and Codebetter.Canvas (Asp.net MVC + NHibernate + DDD). This is a starting point from which more advanced and specialised web applications can grow.
Damian Maclennan, or as these guys might say (2:50) 'some guy', presented a simple framework for web applications at Sydney Alt.Net. Damian's example mixes Asp.Net MVC, Fluent NHibernate and Structure Map.
The Good Parts
My favourite part is the way that NHibernate is configured with the IoC container. The author inherits from the Structure Map Registry class and creates a registry called NHibernateRegistry
internal class NHibernateRegistry : Registry
{
public NHibernateRegistry()
{
ForRequestedType<ISessionFactory>()
.CacheBy(InstanceScope.Singleton)
.TheDefault.Is.ConstructedBy(() => new NHibernateSessionFactory().GetSessionFactory());
ForRequestedType<ISession>()
.CacheBy(InstanceScope.Hybrid)
.TheDefault.Is.ConstructedBy(x => x.GetInstance<ISessionFactory>().OpenSession());
}
}
I think this is a neat way to group IoC registrations. The NHibernateRegistry registers NHibernateSessionFactory as a singleton, which is important because NHibernate requires that the session factory is at application scope. The scope for ISession is InstanceScope.Hybrid which means that the scope of an ISession is one HTTP request, exactly what is required for normal NHibernate usage.
The Missing Parts
It is usually desirable to begin a transaction for each ISession and close the transaction prior to the ISession being closed. If the example is extended to include database writes it will fail without transactions. NHProf recommends that transactions be used even for read-only sessions.
Security can be a challenge in Asp.Net MVC. I would like to see how Damian prefers to secure controllers.
The Repository<T> class currently only supports querying by Id. If a more complicated query is required where should it go? The two popular options are: specialised repositories (PostRepository : IRepository<Post>) with query methods and query objects where each query is encapsulated in an object.
Fluent NHibernate is nice but I wonder why auto mapping was not used. It would work nicely for the domain in the example.
Jerry's Final Thoughts
It's good to see the production of this kind of useful guidance. Someone new to the technologies could easily use Damian's example as a starting point for a well designed Asp.Net MVC application. It is certainly a much more helpful starting point than the Visual Studio template.
Until next time, take care of yourselves and each other.
- Posted by liammclennan on May 21, 2009
Fall back to fluent nhibernate and use the AutoMap<T>.SetAttribute() method. The following example ensures that no two users can ever have the same email address:
private static AutoPersistenceModel Automapping()
{
return AutoPersistenceModel.MapEntitiesFromAssemblyOf<Entity>()
.ForTypesThatDeriveFrom<User>(map => {
map.Map(u => u.EmailAddress).SetAttribute("unique-key", "IX_Unique_EmailAddress");
})
.Where(t => t.Namespace == "Myproject.Domain");
}
- Posted by liammclennan on May 21, 2009
You can't. The trick is to fall back to fluent nhibernate mapping for many-to-many relationships. Also remember to map both sides. Here is an example:
private static AutoPersistenceModel Automapping()
{
return AutoPersistenceModel.MapEntitiesFromAssemblyOf<Entity>()
.ForTypesThatDeriveFrom<User>(map => {
map.HasManyToMany(u => u.Communities).Cascade.All().WithTableName("CommunityMembers");
})
.ForTypesThatDeriveFrom<Community>(map=> map.HasManyToMany(c => c.Users)
.Cascade.All().Inverse().WithTableName("CommunityMembers"))
.Where(t => t.Namespace == "Myproject.Domain");
}
- Posted by liammclennan on May 15, 2009
In general, I like to use enums for model properties that have a fixed set of values, and I like to persist those enums are strings. The advantage is that the values can be read without requiring a pointless mapping.
Because I am using Fluent NHibernate Automapping it was not straightforward to get this to work. The trick is to specifically instruct the automapping to map the enum property. Without this, the property is simply ignored. Here is my automapping configuration, the enum is the User.Role property (line 7):
private static AutoPersistenceModel Automapping()
{
return AutoPersistenceModel.MapEntitiesFromAssemblyOf<Entity>()
.ConventionDiscovery.Add<OneToManyConvention>()
.ConventionDiscovery.Add<NotNullPropertyConvention>()
.WithSetup(s => s.IsBaseType = type => type == typeof(Entity) )
.ForTypesThatDeriveFrom<User>(map => map.Map(u => u.Role))
.Where(t => t.Namespace == "MyNamespace.Domain");
}
The other interesting mapping here is line 6. It instructs the automapper that every domain class inherits from Entity.
- Posted by liammclennan on April 21, 2009
On of the things that I always liked about Ruby on Rails was the low cost of making a change to the data model. Because rails has migrations and because the object-relational mapping is accomplished at runtime using reflection, a new property can be added to a class by simply changing the migration.
In a standard application that uses object-relational mapping making a change to the data model requires three changes: changing the database schema, changing the mapping metadata and changing the domain model. This is bad because:
• having the same information in three places is a violation of the Don't Repeat Yourself (DRY) principle.
• a single change to the application requires three code changes. This is an example of the Shotgun Surgery antipattern.
Fortunately, there is a solution for the .NET developer. Auto Mapping is a part of Fluent NHibernate. It uses conventions and reflection to map a domain model to a database. Combined with NHibernate's schema generation this brings the amount of code changes required to add a new property down to one. When a property is added to a class it is automatically mapped by Fluent NHibernate's Auto Mapper, which means that it is automatically added to the generated database schema.
One of my reservations about NHibernate is that is exposes to many options - making it hard to get started and hard to learn. Automapping has almost the opposite problem. To be efficient your domain model must be reasonably close to the expected conventions. It is possible, but not trivial, to change the conventions.
In my application I changed the convention for mapping one-to-many relationships so that saves, updates and deletes are automatically cascaded. This is done by implementing IHasManyConvention. There are similar interfaces to implement to change the other conventions. Here is my code:
public class OneToManyConvention : IHasManyConvention
{
public bool Accept(IOneToManyPart target)
{
return true;
}
public void Apply(IOneToManyPart target)
{
target.Cascade.All();
}
}
I like Auto Mapping because I like convention over configuration. Even if I have to make some compromises on the mappings it is still worth it for the time that is saved creating and maintaining mappings. Auto mapping can be combined with regular fluent nhibernate for situations where more control is needed. Thank you to the fluent nhibernate team for this excellent library.