What is the best way to provide an AutoMappingOverride for an interface in fluentnhibernate automapp
Posted
by Tom
on Stack Overflow
See other posts from Stack Overflow
or by Tom
Published on 2010-04-13T14:21:29Z
Indexed on
2010/04/19
13:03 UTC
Read the original article
Hit count: 628
In my quest for a version-wide database filter for an application, I have written the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;
using FluentNHibernate.Mapping;
using MvcExtensions.Model;
using NHibernate;
namespace MvcExtensions.Services.Impl.FluentNHibernate
{
public interface IVersionAware
{
string Version { get; set; }
}
public class VersionFilter : FilterDefinition
{
const string FILTERNAME = "MyVersionFilter";
const string COLUMNNAME = "Version";
public VersionFilter()
{
this.WithName(FILTERNAME)
.WithCondition("Version = :"+COLUMNNAME)
.AddParameter(COLUMNNAME, NHibernateUtil.String );
}
public static void EnableVersionFilter(ISession session,string version)
{
session.EnableFilter(FILTERNAME).SetParameter(COLUMNNAME, version);
}
public static void DisableVersionFilter(ISession session)
{
session.DisableFilter(FILTERNAME);
}
}
public class VersionAwareOverride : IAutoMappingOverride<IVersionAware>
{
#region IAutoMappingOverride<IVersionAware> Members
public void Override(AutoMapping<IVersionAware> mapping)
{
mapping.ApplyFilter<VersionFilter>();
}
#endregion
}
}
But, since overrides do not work on interfaces, I am looking for a way to implement this. Currently I'm using this (rather cumbersome) way for each class that implements the interface :
public class SomeVersionedEntity : IModelId, IVersionAware
{
public virtual int Id { get; set; }
public virtual string Version { get; set; }
}
public class SomeVersionedEntityOverride : IAutoMappingOverride<SomeVersionedEntity>
{
#region IAutoMappingOverride<SomeVersionedEntity> Members
public void Override(AutoMapping<SomeVersionedEntity> mapping)
{
mapping.ApplyFilter<VersionFilter>();
}
#endregion
}
I have been looking at IClassmap interfaces etc, but they do not seem to provide a way to access the ApplyFilter method, so I have not got a clue here...
Since I am probably not the first one who has this problem, I am quite sure that it should be possible; I am just not quite sure how this works..
EDIT : I have gotten a bit closer to a generic solution:
This is the way I tried to solve it :
Using a generic class to implement alterations to classes implementing an interface :
public abstract class AutomappingInterfaceAlteration<I> : IAutoMappingAlteration
{
public void Alter(AutoPersistenceModel model)
{
model.OverrideAll(map =>
{
var recordType = map.GetType().GetGenericArguments().Single();
if (typeof(I).IsAssignableFrom(recordType))
{
this.GetType().GetMethod("overrideStuff").MakeGenericMethod(recordType).Invoke(this, new object[] { model });
}
});
}
public void overrideStuff<T>(AutoPersistenceModel pm) where T : I
{
pm.Override<T>( a => Override(a));
}
public abstract void Override<T>(AutoMapping<T> am) where T:I;
}
And a specific implementation :
public class VersionAwareAlteration : AutomappingInterfaceAlteration<IVersionAware>
{
public override void Override<T>(AutoMapping<T> am)
{
am.Map(x => x.Version).Column("VersionTest");
am.ApplyFilter<VersionFilter>();
}
}
Unfortunately I get the following error now :
[InvalidOperationException: Collection was modified; enumeration operation may not execute.]
System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) +51
System.Collections.Generic.Enumerator.MoveNextRare() +7661017
System.Collections.Generic.Enumerator.MoveNext() +61
System.Linq.WhereListIterator`1.MoveNext() +156
FluentNHibernate.Utils.CollectionExtensions.Each(IEnumerable`1 enumerable, Action`1 each) +239
FluentNHibernate.Automapping.AutoMapper.ApplyOverrides(Type classType, IList`1 mappedProperties, ClassMappingBase mapping) +345
FluentNHibernate.Automapping.AutoMapper.MergeMap(Type classType, ClassMappingBase mapping, IList`1 mappedProperties) +43
FluentNHibernate.Automapping.AutoMapper.Map(Type classType, List`1 types) +566
FluentNHibernate.Automapping.AutoPersistenceModel.AddMapping(Type type) +85
FluentNHibernate.Automapping.AutoPersistenceModel.CompileMappings() +746
EDIT 2 : I managed to get a bit further; I now invoke "Override" using reflection for each class that implements the interface :
public abstract class PersistenceOverride<I>
{
public void DoOverrides(AutoPersistenceModel model,IEnumerable<Type> Mytypes)
{
foreach(var t in Mytypes.Where(x=>typeof(I).IsAssignableFrom(x)))
ManualOverride(t,model);
}
private void ManualOverride(Type recordType,AutoPersistenceModel model)
{
var t_amt = typeof(AutoMapping<>).MakeGenericType(recordType);
var t_act = typeof(Action<>).MakeGenericType(t_amt);
var m = typeof(PersistenceOverride<I>)
.GetMethod("MyOverride")
.MakeGenericMethod(recordType)
.Invoke(this, null);
model.GetType().GetMethod("Override").MakeGenericMethod(recordType).Invoke(model, new object[] { m });
}
public abstract Action<AutoMapping<T>> MyOverride<T>() where T:I;
}
public class VersionAwareOverride : PersistenceOverride<IVersionAware>
{
public override Action<AutoMapping<T>> MyOverride<T>()
{
return am =>
{
am.Map(x => x.Version).Column(VersionFilter.COLUMNNAME);
am.ApplyFilter<VersionFilter>();
};
}
}
However, for one reason or another my generated hbm files do not contain any "filter" fields.... Maybe somebody could help me a bit further now ??
© Stack Overflow or respective owner