Friday, 16 July 2010

XML-free convention based compiled mappings WITHOUT Fluent NHibernate – Part 3 –

So in part 2, we came up with code to find and sort our conventions. Now we need some conventions! In my model , I use hilo int Id {get; private set;} properties on my entity classes, mapped to a column called {EntityName} + Id. If you do something different, write your own convention!

First up, the IClassConvention interface:

public interface IClassConvention
{
    void Apply(Type entityType, @class classElement, IEnumerable<Type> entityTypes, hibernatemapping mapping);
}

As you can see, the current entityType and its class mapping are passed in, along with references to the other entity types and the whole mapping document. This lets you do things like check to see if a particular property is an Entity or a component.

public class CreateHiloIdIfTypeHasIntIdProperty : IClassConvention
{
    public void Apply(Type entityType, @class classElement, IEnumerable<Type> entityTypes, hibernatemapping mapping)
    {
        //use reflection to get the Id property from the current class
        var idProperty = entityType.GetProperty("Id", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        if (idProperty != null)
        {  
            //if the id property exists, add a new id element to the @class element
            classElement.id = new id() 
            {
                name = "Id",
                generator = new generator() {@class = "hilo"},
                column = {new column() {name = entityType.Name + "Id"}}
            };
        }
    }
}

Remember, you can write your own convention for this. You could get really clever with this: imagine you use Identity generated columns, but the names are inconsistent in the db – why not look up the table schema and find the identity column and use the name from that?

Here is a convention for turning properties on a type into properties on the @class.

public class CreateBasicProperty : IClassConvention, IRunAfter<CreateHiloIdIfTypeHasIntIdField>, IRunAfter<CreateHiloIdIfTypeHasIntIdProperty>
{
    //list of types. If you need longs, just add them!
    private readonly IList<Type> basicTypes = new[] { typeof(Guid), typeof(Guid?), typeof(int), typeof(int?), typeof(string), typeof(bool), typeof(bool?), typeof(DateTime), typeof(DateTime?) }.ToList().AsReadOnly();
 
    public void Apply(Type type, @class @class, IEnumerable<Type> entityTypes, hibernatemapping mapping)
    {
        var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
            .Where(p => p.Name != @class.id.name) //check its not the Id property!
            .Where(p => basicTypes.Contains(p.PropertyType));
        foreach (var propertyInfo in properties)
        {
            @class.property.Add(new property()
            {
                name = propertyInfo.Name,
            });
        }
    }
}

That’s all for this post! Next time we’ll see a convention for doing many-to-one mappings.

UPDATE: A sample project is available for download on google code. It contains the convention for many-to-one mapping.

0 comments:

Post a Comment