Question

I have a pre-existing database which I'm trying to use Fluent NHibernate automapping with. Many of the tables have audit columns, so what I thought would be reasonable is for many of the entities to inherit a simple abstract class like this :

public abstract class AuditableEntity
{
    public virtual string   CreatedBy { get; set; }
    public virtual DateTime CreatedOn { get; set; }
    public virtual string   ModifiedBy { get; set; }
    public virtual DateTime ModifiedOn { get; set; }
}

The problem arises because these values are set in the database, and if I try to insert a new entity without setting these values I get a SqlTypeException : SqlDateTime overflow because, being a value type, DateTime is initialised to DateTime.MinValue.

What I tried to do to fix this was add an automapping override like this :

public class AuditableEntityOverride : IAutoMappingOverride<AuditableEntity>
{
    public void Override(AutoMapping<AuditableEntity> mapping)
    {
        mapping.Map(x => x.CreatedBy).Generated.Insert();
        mapping.Map(x => x.CreatedOn).Generated.Insert();
        mapping.Map(x => x.ModifiedBy).Generated.Always();
        mapping.Map(x => x.ModifiedOn).Generated.Always();
    }
}

However, this doesn't actually do anything!

I can fix the problem by explicitly overriding the mapping for each entity which inherits the audit columns, but that is quite a lot of entities and I'm trying to let Fluent NHibernate do most of the work here.

Is there something I can do to enforce this mapping upon all subclasses?

Update

In case it helps, this is how I'm creating the session factory. Maybe there's something I'm missing here as well :

var sessionFactory = Fluently.Configure(new Configuration().AddProperties(new Dictionary<string, string>
{
    {Environment.ConnectionDriver,typeof (SqlClientDriver).FullName},
    {Environment.Dialect,typeof (MsSql2008Dialect).FullName},
    {Environment.ConnectionProvider,typeof (DriverConnectionProvider).FullName},
    {Environment.ConnectionString, connectionString},
    {Environment.ShowSql, "true"},
    {Environment.BatchSize, "100"},
    {Environment.Isolation,"ReadCommitted"}
}))
.Mappings(map => map.AutoMappings.Add(AutoMap.AssemblyOf<Transaction>(new AutomappingConfiguration())
                                            .Conventions.AddFromAssemblyOf<ReferenceConvention>()
                                            .Conventions.Add(DynamicUpdate.AlwaysTrue())
                                            .Conventions.Add(DynamicInsert.AlwaysTrue())
                                            .UseOverridesFromAssemblyOf<CurrencyOverride>))
.BuildSessionFactory();
Was it helpful?

Solution

You should actually be able to write your own convention by implementing the IPropertyConvention interface and checking against the instance names, for example:

public class DefaultAutiableConvention: IPropertyConvention
{
    public void Apply(IPropertyInstance instance)
    {
        if (instance.Name == "CreatedBy" || instance.Name == "CreatedOn")
        {
            instance.Generated.Insert();
        }
        else if (instance.Name == "ModifiedBy" || instance.Name == "ModifiedOn")
        {
            instance.Generated.Always();
        }
    }
}

And then add that convention also to your configuration

.Conventions.Add(new DefaultAutiableConvention())
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top