Fluent NHibernate
Auto Mapping Conventions

Auto mappings are generated based on a set of conventions, assumptions about your environment, that mean you can map your entire domain with a miniscule amount of code. Sometimes however, the conventions we supply are not to your liking, perhaps you're a control freak and want 100% control, or more likely you're working against an existing database that has it's own set of standards. You'd still like to use the auto mapper, but can't because it maps your entities all wrong.

Luckily for you we've thought about that, you can customise the conventions that the auto mapper uses. To see more about conventions in general, read the Conventions page.

We'll continue with our store example from before (in Auto Mapping), which comprised of a Product and a Shelf.

public class Product  
{  
  public int Id { get; private set; }  
  public virtual string Name { get; set; }  
  public virtual decimal Price { get; set; }  
}  

public class Shelf  
{  
  public virtual int Id { get; private set; }  
  public virtual IList<Product> Products { get; private set; }  

  public Shelf()
  {
    Products = new List<Product>();
  }
}

Using the standard auto mapping conventions, this assumes a database schema like so:

table Product (
  Id int identity primary key,
  Name varchar(100),
  Price decimal,
  Shelf_id int foreign key
)

table Shelf (
  Id int identity primary key
)

Nothing too complicated there. The auto mapper has correctly assumed that our Ids are identity's and are primary keys, it's also assumed their names, the name of our foreign key to the Shelf table (ShelfId), and the length of our Name column.

Lets assume for the sake of this post that you're not happy with that schema. You're one of those people that prefers to name their primary key after the table it's in, so our Product identity should be called ProductId; also, you like your foreign key's to be explicitly named _FK, and your strings are always a bit longer than 100.

Remember this fellow?

AutoPersistenceModel.MapEntitiesFromAssemblyOf<Product>()  
  .Where(t => t.Namespace == "Storefront.Entities");

Lets update it to include some convention overrides. We'll start with the Id name. The conventions we're about to implement are better explained in the Conventions wiki.

AutoPersistenceModel.MapEntitiesFromAssemblyOf<Product>()
  .Where(t => t.Namespace == "Storefront.Entities")
  .ConventionDiscovery.Add<PrimaryKeyConvention>();

We've added a convention to the convention discovery mechanism, now let's implement it.

public class PrimaryKeyConvention
  : IIdConvention
{
  public bool Accept(IIdentityPart id)
  {
    return true;
  }

  public void Apply(IIdentityPart id)
  {
    id.ColumnName(id.Property.ReflectedType.Name + "Id");
  }
}

Our PrimaryKeyConvention gets applied to all Ids, and sets their column name based on the ReflectedType of the Id property. This is a fancy way of saying get the name of the class the Id property is in. Our primary key's will now be generated as TypeNameId; which means our schema now looks like this:

table Product (
  ProductId int identity primary key,
  Name varchar(100),
  Price decimal,
  Shelf_id int foreign key
)

table Shelf (
  ShelfId int identity primary key
)

As you can see, our primary key's now have our desired naming convention. Lets do the other two together, as they're so simple; we'll override the foreign key naming, and change the default length for strings.

.ConventionDiscovery.Setup(c =>
  c.Add<PrimaryKeyConvention>();
  c.Add<ForeignKeyConvention>();
  c.Add<DefaultStringLengthConvention>();
});

public class ForeignKeyConvention
  : IHasManyConvention
{
  public bool Apply(IOneToManyPart part)
  {
    return true;
  }

  public void Accept(IOneToManyPart part)
  {
    part.WithKeyColumn(part.ParentType.Name + "_FK");
  }
}

public class DefaultStringLengthConvention
  : IPropertyConvention
{
  public bool Apply(IProperty property)
  {
    return true;
  }

  public void Accept(IProperty property)
  {
    part.WithLengthOf(250);
  }
}

That's all there is to it, when combined with the other conventions you can customise the mappings quite heavily while only adding a few lines to your auto mapping.

This is our final schema:

table Product (
  ProductId int identity primary key,
  Name varchar(250),
  Price decimal,
  Shelf_FK int foreign key
)

table Shelf (
  ShelfId int identity primary key
)

To see more about conventions, read the Conventions and Converting To New Style Conventions pages.

What next? Auto Mapping Type Conventions