I have been working on these for a while now. It all started when I had to adapt to DBA style naming conventions. I wanted to automate everything because 16 tables with almost the same number of columns becomes a lot of text to write . Yeah I have to override a column name every now and then but all in all it has served me pretty well. Later I added some conventions for invert many-to-many and some other conventions as well.
I’ll just add them here so let the games begin:
public class TableNameConvention : IClassConvention, IClassConventionAcceptance
{
public void Apply(IClassInstance instance)
{
instance.Table("`" + Inflector.Underscore(instance.EntityType.Name) + "´");
}
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => x.TableName, Is.Not.Set);
}
}
I recently posted the Inflector class and this and much more is what I have been using it for. The method “Inflector.Underscore” makes SomeEntity into some_entity. It should be easy enough to understand :) Oh and the reason for the acceptance criteria is for those few times I got complaints about the table names and needed to set it in the mapping. It sets the table name if it has not been set from the mapping.
public class ManyToManyTableName : ManyToManyTableNameConvention
{
protected override string GetBiDirectionalTableName(IManyToManyCollectionInspector collection,
IManyToManyCollectionInspector otherSide)
{
return Inflector.Underscore(collection.EntityType.Name + "_" + otherSide.EntityType.Name);
}
protected override string GetUniDirectionalTableName(IManyToManyCollectionInspector collection)
{
return Inflector.Underscore(collection.EntityType.Name + "_" + collection.ChildType.Name);
}
}
This one was a bit difficult for me to get right, had to do a few trials before I got it correct.
public class EnumConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Apply(IPropertyInstance instance)
{
instance.CustomType(instance.Property.PropertyType);
}
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Property.PropertyType.IsEnum);
}
}
My trusty old EnumConvention, it has made developing against a RDBMS a lot nicer. Enums are a powerful addition to the model.
public class IdColumnConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
instance.Column(Inflector.Underscore(instance.EntityType.Name) + "_id");
instance.GeneratedBy.Identity();
}
}
The IdColumnConvention is the only one that changes between projects and it’s the generated by that is subject to change. You might want to throw in a IIdConventionAcceptance if you have more than one type of Id column.
public class PropertyConvention : IPropertyConvention
{
public void Apply(IPropertyInstance instance)
{
instance.Column(Inflector.Underscore(instance.Property.Name));
}
}
Nothing too exciting here either, just the naming of the columns.
public class CustomForeignKeyConvention : ForeignKeyConvention
{
protected override string GetKeyName(Member property, Type type)
{
if (property == null)
return Inflector.Underscore(type.Name) + "_id";
return Inflector.Underscore(property.DeclaringType.Name) + "_id";
}
}
This one on the other hand is a different story. Here is where conventions become convenient (and difficult). It’s really nice to be able to set the foreign key but figuring out the correct variable to put in took some time. The last return is for many-to-one and the top one is for all the others.
public class HasManyConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Cascade.All();
instance.Inverse();
instance.Key.Column(Inflector.Underscore(instance.EntityType.Name + "_id"));
instance.Key.ForeignKey(string.Format("fk_{0}_{1}",
Inflector.Underscore(instance.EntityType.Name).ToLower(),
Inflector.Underscore(instance.ChildType.Name).ToLower()));
}
}
I set cascade and inverse here as well as the names of the foreign key and the column name. I probably don’t need the column name though. If you want to customize the inverse and cascading you would be better of moving that over to a second convention so you can pass in a convention acceptance criteria to decide whether to set it or not.
public class ReferenceConvention : IReferenceConvention
{
public void Apply(IManyToOneInstance instance)
{
instance.Cascade.None();
instance.Column(Inflector.Underscore(instance.Property.PropertyType.Name) + "_id");
instance.ForeignKey(
string.Format("fk_{0}_{1}",
Inflector.Underscore(instance.Property.PropertyType.Name).ToLower(),
Inflector.Underscore(instance.EntityType.Name).ToLower()));
}
}
The last one for today is pretty similar to the HasMany convention. When working with ASP.NET MVC it’s really important to set the cascading to none in the following scenario:
Let’s say you have and order that holds products. You retrieve the order from the database. Then you add a product to the order and want to use the cascading inverse functionality in the Order mapping. However if your product holds a reference to the Order in your View (you use your data model in your presentation layer) you could end up with the following message “a different object with the same identifier value was already associated with the session”. What this means is that you have cascading on the wrong end. Cascading from the wrong side of things is as bad as leaving your aggregate members as publicly accessible as the aggregate root.