Fluent NHibernate
Auto Mapping Conventions (changes)

Showing changes from revision #1 to #2: Added | Removed

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.

What exactlyTo issee mappedmore usingabout conventions?conventions Asin ofgeneral, r190: Default lazy load, cacheability, string length, ids, key names, foreign key column names, table names, many-to-many table names, version column names, and a wealth of specific property, one-to-one, one-to-many, and many-to-many overrides.

Although we do allow you to customise a lot of things, not everything is covered yet. If you do encounter a scenario you can't handle, drop us a message onread the mailingConventions list, or even better: supply us a patch.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")
  .WithConvention(convention.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)
  {
    convention.GetPrimaryKeyNameFromTypereturn =
      typetrue;
  }

  public =>void type.NameApply(IIdentityPart id)
  {
    id.ColumnName(id.Property.ReflectedType.Name + "Id";"Id");
  });}
}

WhatOur wePrimaryKeyConvention didgets thereapplied wasto useall Ids, and sets their column name based on the WithConventionReflectedType method to customiseof the ConventionId instanceproperty. thatThis is a fancy way of saying get the autoname mapper uses. In this case we overwroteof the GetPrimaryKeyNameFromTypeclass functionthe withId ourproperty ownis lambdain. expression; as per our function, ourOur 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
)

The convention functions are called against their respective mapping part for every generated entity, and the result of their execution is used to generate the mappings. The part that they work against is usually discernible from their name, or their parameters. GetTableName for example works against an entity Type, while GetVersionColumnName is called against every PropertyInfo gleaned from your entities. As there is no API documentation (as of writing), it's a matter of intellisense poking to find which conventions are applicable to what you want to override.

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.

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

public class ForeignKeyConvention
  : IHasManyConvention
{
  convention.GetPrimaryKeyNameFromTypepublic =bool Apply(IOneToManyPart part)
  {
    typereturn =>true;
  }

  public type.Namevoid Accept(IOneToManyPart part)
  {
    part.WithKeyColumn(part.ParentType.Name + "Id";"_FK");
  convention.GetForeignKeyNameOfParent}
}

public =class DefaultStringLengthConvention
  : IPropertyConvention
{
  public bool Apply(IProperty property)
  {
    typereturn =>true;
  }

  public type.Namevoid +Accept(IProperty "_FK";property)
  convention.DefaultStringLength = 250;{
    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