Fluent NHibernate Wiki
Im>JamesGregory
No edit summary
Im>JamesGregory
No edit summary
Line 87: Line 87:
 
== Configuration ==
 
== Configuration ==
   
Now that we've created our convention, we need to inform Fluent NHibernate of how to use it. The simplest way to do this is just to use the [[Fluent configuration]] mapping setup to use all conventions in an assembly, alternatively you can just add individual conventions. Both these are done through the <code>ConventionDiscovery</code> property of the fluent mappings setup.
+
Now that we've created our convention, we need to inform Fluent NHibernate of how to use it. The simplest way to do this is just to use the [[Fluent configuration]] mapping setup to use all conventions in an assembly, alternatively you can just add individual conventions. Both these are done through the <code>Conventions</code> property of the fluent mappings setup.
   
 
<source lang="csharp">
 
<source lang="csharp">
Line 96: Line 96:
 
m.FluentMappings
 
m.FluentMappings
 
.AddFromAssemblyOf<Entity>()
 
.AddFromAssemblyOf<Entity>()
.ConventionDiscovery.AddFromAssemblyOf<LowercaseTableNameConvention>();
+
.Conventions.AddFromAssemblyOf<LowercaseTableNameConvention>();
 
})
 
})
 
</source>
 
</source>

Revision as of 22:43, 3 August 2009

Conventions are small self-contained chunks of behavior that are applied to the mappings Fluent NHibernate generates. These conventions are of varying degrees of granularity, and can be as simple or complex as you require. You should use conventions to avoid repetition in your mappings and to enforce a domain-wide standard consistency.

The conventions are built using a set of interfaces that each define a single method, Apply, with varying parameters based on the kind of convention you're creating; this method is where you make the changes to the mappings.

By default conventions are applied before your mappings are created from the fluent mappings. What this means is you define the "shape" of your mappings first, then the conventions are applied to set all default values, and finally any explicit changes you made with the fluent mappings are applied.

Given this mapping:

public class PersonMap : ClassMap<Person>
{
  public PersonMap()
  {
    Id(x => x.Id);
    Map(x => x.Name);
      .Column("FullName")
      .Length(100);
    Map(x => x.Age);
  }
}

The convention workflow is:

  1. Determine the shape of your mapping by looking at what high-level methods you called. With the PersonMap example, the shape is considered to be: ClassMap of Person, with an Id, and two Property's (Name and Age respectively); the Column and Length calls do not form a part of this shape and are purely value altering calls, these are ignored for the time being.
  2. Conventions are then applied to this shape, setting any values they wish; column names, lengths, cascades, access strategies, you name it.
  3. Then the value altering methods from the fluent mappings are applied; in this case the Column and Length methods will then be applied.

Your first convention

One of the most common conventions you'll use is for classes, we'll start by using that as an example. class conventions are used to alter any value properties on the ClassMap<T> itself, if you wanted to alter specific properties or relationships you'd use their respective conventions from the available interfaces.

public class LowercaseTableNameConvention : IClassConvention
{
  public void Apply(IClassInstance instance)
  {
    // alterations
  }
}

That's an empty class convention! Lets make it do something useful. For a simple example we'll set a default table name for our entities; we'll simulate some daft archaic rule that all table names must be prefixed with tbl_.

public void Apply(IClassInstance instance)
{
  classMap.Table("tbl_" + classMap.Type.Name);
}

Simple. The IClassInstance interface contains several properties that allow you to inspect what has been set on the mapping, and usefully gives you access to the underlying System.Type that the mapping is using. We can then use that information to change the table name using the Table method.

Conditional applying of conventions

See: Acceptance criteria

Sometimes it may be necessary to control when a convention is applied; you may want a convention to only act on a subset of the mappings that it would normally alter. You can do this by implementing an additional interface that exposes an Accept method which allows you to define the criteria for which it will be applied.

The additional interface you need to implement has the same name as your main convention interface, with "Acceptance" on the end. So IClassConvention would have an IClassConventionAcceptance interface.

public class LowercaseTableNameConvention : IClassConvention, IConventionAcceptance<IClassInspector>
{
  public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
  {
    // alterations
  }

  /* snip */

The IAcceptanceCriteria instance is what you use to setup various expectations, for more information about the criterias you can create see: Acceptance criteria.

public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
  criteria.Expect(x => x.Type != typeof(SomeSpecialCase));
}

That example simply changes the convention so that it won't be applied to any mappings of the SomeSpecialCase type.

Not specifying any expectations is the same as not creating an Accept.

Configuration

Now that we've created our convention, we need to inform Fluent NHibernate of how to use it. The simplest way to do this is just to use the Fluent configuration mapping setup to use all conventions in an assembly, alternatively you can just add individual conventions. Both these are done through the Conventions property of the fluent mappings setup.

Fluently.Configure()
  .Database(/* database config */)
  .Mappings(m =>
  {
    m.FluentMappings
      .AddFromAssemblyOf<Entity>()
      .Conventions.AddFromAssemblyOf<LowercaseTableNameConvention>();
  })

Alternatively you can use just Add to add an instance or a type, or you can use the Setup method to call a lambda that can be used to supply multiple different conventions (e.g. some from an assembly, and some individually).

Once you've set that up, Fluent NHibernate will automatically call your convention when it's generating the mappings.

Note on previous conventions

If you were using our previous conventions API, then you may be interested in converting to new style conventions, which explains the various differences you may encounter; however, there's nothing difficult about it, you just need to pick the right interface. If you're not quite comfortable with this change yet, the convention shortcuts might be of some use.