Fluent NHibernate Wiki
Im>JamesGregory
(Created page with 'Fluent NHibernate has a concept called Auto Mapping, which is a mechanism for automatically mapping all your entities based on a set of conventions. Auto mapping utilises the pr…')
 
Im>JamesGregory
No edit summary
Line 86: Line 86:
   
 
That's all there is to automatically mapping your domain entities. It's all a lot easier than writing out mappings, isn't it? There's much more to the auto mapping that I haven't covered here, and I hope to write about those soon. Until then, enjoy!
 
That's all there is to automatically mapping your domain entities. It's all a lot easier than writing out mappings, isn't it? There's much more to the auto mapping that I haven't covered here, and I hope to write about those soon. Until then, enjoy!
  +
  +
== Components ==
  +
  +
The Fluent NHibernate \AutoMapper is capable of automatically recognising simple components; currently only properties are supported, no relationships inside the components. With that in mind, I'll walk through how to automap your components.
  +
  +
Lets imagine this database structure:
  +
  +
<source lang="sql">
  +
table Person (
  +
Id int primary key,
  +
Name varchar(200),
  +
Address_Number int,
  +
Address_Street varchar(100),
  +
Address_PostCode varchar(8)
  +
)
  +
</source>
  +
  +
We want to map that to the following model:
  +
  +
<source lang="csharp">
  +
public class Person
  +
{
  +
public virtual int Id { get; private set; }
  +
public virtual string Name { get; set; }
  +
public virtual Address Address { get; set; }
  +
}
  +
  +
public class Address
  +
{
  +
public int Number { get; set; }
  +
public string Street { get; set; }
  +
public string PostCode { get; set; }
  +
}
  +
</source>
  +
  +
With this design, `Address` is actually a component, which isn't a full entity, more of a way of providing a clean model to a normalised database structure. I'll get started by setting up the auto-mapper with the FluentConfiguration and DatabaseConfiguration APIs.
  +
  +
<source lang="csharp">
  +
var sessionFactory = Fluently.Configure()
  +
.Database(MsSqlConfiguration.MsSql2005
  +
.ConnectionString(c => c
  +
.Is(ApplicationConnectionString)))
  +
.Mappings(m =>
  +
m.AutoMappings.Add(
  +
AutoPersistenceModel.MapEntitiesFromAssemblyOf<Person>()
  +
.Where(type => type.Namespace.EndsWith("Domain"))
  +
))
  +
.BuildSessionFactory();
  +
</source>
  +
  +
We've now got the auto mappings integrated with NHibernate, so we need to instruct the auto mapper how to identify components; after the `Where` call, we can add a call to `WithSetup` to help the auto mapper identify our components.
  +
  +
<source lang="csharp">
  +
.WithSetup(s =>
  +
{
  +
s.IsComponentType =
  +
type => type == typeof(Address);
  +
})
  +
</source>
  +
  +
The `IsComponentType` convention is what Fluent NHibernate uses to determine whether a type is one that will be mapped as a component, rather than a full entity.
  +
  +
There are two things you need to know about this convention:
  +
  +
# You can only set this convention once, so you'll need to find a way that allows you to identify multiple components; there are several options to this, including using the namespace (like `Where` does), or checking a suffix on the type name (anything that ends in "Component", for example).
  +
# This is not an exclusive call, so you need to segregate your component types from your standard entity types (so they'll be excluded by the `Where` call), otherwise they'll be auto-mapped as full entities as well as components - ''not good''.
  +
  +
With that, the `Address` should now be automatically mapped as a component; the auto mapper will pick up the three properties and map them as properties on the component.
  +
  +
There's one more thing, for illustrative purposes I've deliberately gone against Fluent NHibernate's inbuilt convention for naming columns. By default any columns mapped in a convention will be prefixed with their type name, so `Address.Street` would be mapped against a column called `AddressStreet`; this is my personal preference, but not what our database contains! We can control how our columns are named by altering the `GetComponentColumnPrefix` convention, like so:
  +
  +
<source lang="csharp">
  +
.WithSetup(s =>
  +
{
  +
s.IsComponentType =
  +
type => type == typeof(Address);
  +
s.GetComponentColumnPrefix =
  +
type => type.Name + "_";
  +
})
  +
</source>
  +
  +
The convention now specifies that columns should be named \TypeName\_\PropertyName, so `Address.Street` is now correctly mapped against `Address_Street`.

Revision as of 11:54, 13 August 2009

Fluent NHibernate has a concept called Auto Mapping, which is a mechanism for automatically mapping all your entities based on a set of conventions.

Auto mapping utilises the principal of convention over configuration. Using this principal, the auto mapper inspects your entities and makes assumptions of what particular properties should be. Perhaps you have a property with the name of `Id` and type of `int`, the auto mapping might (and will by default) decide that this is an auto-incrementing primary key.

By using the auto mappings, you can map your entire domain with very little code, and certainly no XML. There are still scenarios where it may not be suitable to use the auto mapping, at which point it would be more appropriate to use the standard mapping; however, for most greenfield applications (and quite a few brownfield ones too) auto mapping will be more than capable.

Getting started with a simple example

Although it isn't the purpose of this post give an in-depth walkthrough of Auto Mapping, it's not out of scope for a simple example! So I'll go through how to map a simple domain using the Fluent NHibernate \AutoMapper.

Imagine what the entities might be like for a simple shop; in-fact, let me show you.

public class Product
{
  public virtual 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>();
  }
}

You can't get much simpler than that. We've got a product, with an auto-incrementing primary key called `Id`, a `Name` and a `Price`. The store has some shelves it fills with products, so there's a `Shelf` entity, which has an `Id` again, and a list of the `Product`s on it; the `Product` collection is a *one-to-many* or *Has Many* relationship to the `Product`.

I'm going to make the assumption here that you have an existing NHibernate infrastructure, if you don't then it's best you consult a general NHibernate walkthrough before continuing.

We're going to be using the `AutoPersistenceModel` to do our mapping, which you can use in combination with the FluentConfiguration API. To begin with we should take a look at the static `MapEntitiesFromAssemblyOf<T>` method; this method takes a generic type parameter from which we determine which assembly to look in for mappable entities.

AutoPersistenceModel.MapEntitiesFromAssemblyOf<Product>();

That's it, you've mapped your domain... Ok, there might be a little bit more to do than that. Let me explain.

The `MapEntitiesFromAssemblyOf<T>` method creates an instance of an `AutoPersistenceModel` that's tied to the assembly that `Product` is declared. No mappings are actually generated until you come to your entities into NHibernate.

We're going to utilise the fluent configuration API to create our `SessionFactory`.

var sessionFactory = Fluently.Configure()
  .Database(MsSqlConfiguration.MsSql2005
    .ConnectionString(c => c
      .Is(ApplicationConnectionString)))
  .Mappings(m =>
    m.AutoMappings.Add(AutoPersistenceModel.MapEntitiesFromAssemblyOf<Product>())
  )
  .BuildSessionFactory();

You can see that our `AutoPersistenceModel` is sat in the middle of the configuration, inside the `Mappings` call; this is our way of telling Fluent NHibernate that we're using the Auto Mapper.

We're now capable of getting NHibernate to accept our auto mapped entities, there's just one more thing we need to deal with. The auto mapper doesn't know which classes are your entities, and which ones are your services (and everything else). The setup we're using above simply maps every class in your assembly as an entity, which isn't going to be very useful; so I'll introduce one final method: `Where(Func<Type, bool>)`.

The `Where` method takes a lambda expression which is used to limit types based on your own criteria. The most common usage is limiting based on a namespace, but you could also look at the type name, or anything else exposed on the `Type` object.

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

Bringing all that together leaves us with this NHibernate setup:

var sessionFactory = Fluently.Configure()
  .Database(MsSqlConfiguration.MsSql2005
    .ConnectionString(c => c
      .Is(ApplicationConnectionString)))
  .Mappings(m =>
    m.AutoMappings.Add(
      AutoPersistenceModel.MapEntitiesFromAssemblyOf<Product>()
        .Where(t => t.Namespace == "Storefront.Entities")
    ))
  .BuildSessionFactory();

That's all there is to automatically mapping your domain entities. It's all a lot easier than writing out mappings, isn't it? There's much more to the auto mapping that I haven't covered here, and I hope to write about those soon. Until then, enjoy!

Components

The Fluent NHibernate \AutoMapper is capable of automatically recognising simple components; currently only properties are supported, no relationships inside the components. With that in mind, I'll walk through how to automap your components.

Lets imagine this database structure:

table Person (
  Id int primary key,
  Name varchar(200),
  Address_Number int,
  Address_Street varchar(100),
  Address_PostCode varchar(8)
)

We want to map that to the following model:

public class Person
{
  public virtual int Id { get; private set; }
  public virtual string Name { get; set; }
  public virtual Address Address { get; set; }
}

public class Address
{
  public int Number { get; set; }
  public string Street { get; set; }
  public string PostCode { get; set; }
}

With this design, `Address` is actually a component, which isn't a full entity, more of a way of providing a clean model to a normalised database structure. I'll get started by setting up the auto-mapper with the FluentConfiguration and DatabaseConfiguration APIs.

var sessionFactory = Fluently.Configure()
  .Database(MsSqlConfiguration.MsSql2005
    .ConnectionString(c => c
      .Is(ApplicationConnectionString)))
  .Mappings(m =>
    m.AutoMappings.Add(
      AutoPersistenceModel.MapEntitiesFromAssemblyOf<Person>()
        .Where(type => type.Namespace.EndsWith("Domain"))
  ))
  .BuildSessionFactory();

We've now got the auto mappings integrated with NHibernate, so we need to instruct the auto mapper how to identify components; after the `Where` call, we can add a call to `WithSetup` to help the auto mapper identify our components.

.WithSetup(s =>
{
  s.IsComponentType =
    type => type == typeof(Address);
})

The `IsComponentType` convention is what Fluent NHibernate uses to determine whether a type is one that will be mapped as a component, rather than a full entity.

There are two things you need to know about this convention:

  1. You can only set this convention once, so you'll need to find a way that allows you to identify multiple components; there are several options to this, including using the namespace (like `Where` does), or checking a suffix on the type name (anything that ends in "Component", for example).
  2. This is not an exclusive call, so you need to segregate your component types from your standard entity types (so they'll be excluded by the `Where` call), otherwise they'll be auto-mapped as full entities as well as components - not good.

With that, the `Address` should now be automatically mapped as a component; the auto mapper will pick up the three properties and map them as properties on the component.

There's one more thing, for illustrative purposes I've deliberately gone against Fluent NHibernate's inbuilt convention for naming columns. By default any columns mapped in a convention will be prefixed with their type name, so `Address.Street` would be mapped against a column called `AddressStreet`; this is my personal preference, but not what our database contains! We can control how our columns are named by altering the `GetComponentColumnPrefix` convention, like so:

.WithSetup(s =>
{
  s.IsComponentType =
    type => type == typeof(Address);
  s.GetComponentColumnPrefix =
    type => type.Name + "_";
})

The convention now specifies that columns should be named \TypeName\_\PropertyName, so `Address.Street` is now correctly mapped against `Address_Street`.