Fluent NHibernate
Conventions Code

If you've read over Conventions and are curious as to how it all fits together, then hopefully this page should shed some light on it.

Obviously the specifics of this may change, so I'll try to keep as general as possible about it all.

Conventions are simply classes that can alter the mappings before they're handed off to NHibernate. They can do anything to the mappings that their specific interfaces allow. Each convention interface is associated with a particular "mapping part", which are analogous to the various xml elements that comprise the traditional mappings.

A very important part of how the conventions work is the IConventionFinder interface and it's implementation (DefaultConventionFinder). This service is (as it's name implies) used to find conventions, you give it some criteria to search and it'll find any conventions in there that you request.

Any implementors of the Conventions Interfaces have the ability to specify a special constructor, ctor(IConventionFinder), that will allow their convention to be injected with the IConventionFinder.

So, bringing those two together, we have the discovery built-in conventions that Fluent NHibernate uses to execute the hierarchy of conventions. Yes, we use conventions to create the conventions! Lets look at a typical example:

public class PropertyDiscoveryConvention
  : IMappingPartConvention
{
  private readonly IConventionFinder conventionFinder;

  public PropertyDiscoveryConvention(IConventionFinder conventionFinder)
  {
    this.conventionFinder = conventionFinder;
  }

  public bool Accept(IMappingPart part)
  {
    return (part is IProperty);
  }

  public void Apply(IMappingPart part)
  {
    var conventions = conventionFinder.Find<IPropertyConvention>();
    var property = (IProperty)part;

    foreach (var convention in conventions)
    {
      if (convention.Accept(property))
        convention.Apply(property);
    }
  }
}

This is the PropertyDiscoveryConvention that is actually used in calling any implementors of the IPropertyConvention interface. There is a class like this for each of the different conventions.

It's implementation can easily be described as:

  1. (in Accept) Accepts only IMappingParts that are actually IPropertys
  2. (in Apply) Finds any IPropertyConvention implementors in the locations that the IConventionFinder has been told to look
  3. Iterates the found conventions, seeing if each will accept the property, and applying them if they do

This is the pattern that all the discovery classes use, just substitute the mapping part and the particular convention for others. For example the discovery class for Versions simply accepts only if the part is an IVersion, and finds any IVersionConvention implementors.

There's still the matter of how the conventions actually get started, and this is the one touch-point that the conventions have with the mapping generation. Somewhere deep in the PersistenceModel, once all the mappings are generated, the IConventionFinder is called to find any implementors of IEntireMappingsConvention. With each found convention being called in the same pattern as the discovery classes above, but instead of on individual mapping parts they're called on an IEnumerable of all the class maps.