Automatically mapping properties in Fluent NHibernate - fluent-nhibernate

I'm taking a complicated legacy schema and mapping it with Fluent NHibernate. The schema is wacky enough that I've given up on automapping; the relationships between tables are weird and complicated, and it would involve a ton of exceptions.
The thing is, the simple properties are totally normal; a table's Title column maps to that entity's Title property, and so on. But because I've opted out of global automapping, there doesn't seem to be a way to avoid mapping each of my string and integer properties on every class. I find myself wanting something like
class SomeMapping : ClassMap<SomeEntity>
{
public SomeMapping()
{
MapEverythingSimple();
}
}
Before I build something complicated that reflectively emits lambda expressions (or somesuch), am I just missing an obvious feature?

How about using automapping and then overrides where things don't fit conventions?
I don't think it's too much of a burden. You'll need to specify the complex relationships
that don't fit a convention somewhere anyhow.

Or you can try NHibernate Mapping Generator to generate NHibernate mapping files and corresponding domain classes from existing DB tables:
http://nmg.codeplex.com/

Use the trial version of Visual NHibernate to quickly generate your entity classes and Fluent mappings, then take it from there. Disclaimer: I work for Slyce Software.

This is not what I ended up doing, but for posterity, here's how you can map properties automatically without using an automapper:
public class PropMap<V> : ClassMap<V>
{
public PropMap()
{
foreach (var propInfo in typeof(V).GetProperties()
.Where(p => simpleTypes.Contains(p.PropertyType)))
{
ParameterExpression param = Expression.Parameter(typeof(V), "x");
Map(Expression.Lambda<Func<V, object>>(
Expression.Convert(Expression.MakeMemberAccess(param, propInfo), typeof(object)), param));
}
}
private static readonly Type[] simpleTypes = new[]
{
typeof (DateTime),
typeof (String),
typeof (int),
typeof (long),
typeof (Enum)
};
}
And then just have the map classes inherit from that. Obviously, it has some serious flaws, and I didn't go with it.

Related

meta program fluent validation

I have used fluent validation for hard code validations like this:
RuleFor(customer => customer.CreditLimit).GreaterThan(customer => customer.MinimumCreditLimit);
I guess it would not be a problem to replace MinimumCreditLimit by some (meta) database driven value in the code. Did someone ever attempt this and what would be the best practises in this context (apart from the fact that MinimumCreditLimit could stem from some strategy design pattern). Could one potentially use expression trees against fluent validation to make it even more meta program-ish?
Well, the easiest way would be to add a ctor to your Validation class.
public class EntityValidator : AbstractValidator<Entity> {
public EntityValidator(int minimumCreditLimit) {
Rulefor(customer => customer.CreditLimit).GreaterThan(minimumCreditLimit);
}
}
now you could use it like that (if you don't use the "attributes" way).
var minimumCreditLimit = <GetTheLimitFromDbWithAnyMethod>();
var validator = new EntityValidator(minimumCreditLimit);
<yourEntityInstance>.ValidateAndThrow(validator);
Another (similar) way would be to pass some way to get data from db to your validator (in ctor for example), and create a custom validator / extension method to use this.

How to configure Fluent NHibernate automapping so it makes separate hbm for sub classes?

This question might seems a bit strange at first but there's a legacy project that is working this way and I want to know if there's a way to generate its hbm documents using Fluent Nhibernate.
We have a parent class which is not an abstract class .Something like this:
[Entity("EmployeeTable")]
public class Employee
{
//Memebers of Employee
}
and it has some subclasses.The purpose of these subclasses is merely for code re-usability and as you can see these are some views (Summaries) to represent some information.
[Entity("EmployeeType1View")]
public class EmployeeType1:Employee
{
//Memebers of EmployeeType1
}
[Entity("EmployeeType2View")]
public class EmployeeType2:Employee
{
//Memebers of EmployeeType2
}
So here is the question : is there a way that we can tell fluent nhibernate not to take this inheritance hierarchy into account or in another word to tell it to generate separate hbm file for each of these classes?
unfortunatly FNH can not write subclass maps individually. You could however alter the mapping after writing it to disc.
var model = new FluentNHibernate.Automapping.AutoPersistenceModel();
// add assembly and the like to model
model.WriteMappingsTo(path);
forech(var baseclass in classesWithSubclasses)
{
var doc = new XmlDocument();
doc.Load(baseclass.getType().FullName + ".hbm.xml");
// use xpath to separate the subclassmapping in its own file
}

How can I detect NHibernate HasManyToMany mapping at run-time?

I am trying to detect HasManyToMany relationships in entities at run-time for testing purposes. I've had many problems with people writing bad mappings so I wrote a test to test every single mapping on our continuous integration server.
Right now I can test every entity, composite and non-composite by detecting the mapped Id property(s) and calling .Get() or a composite getter. Most of which is done using reflection. I am using GetClassMetadata while going over every entity type.
But I missed testing something that was broken, a HasManyToMany.
I am using Fluent NHibernate, and the mapping is:
mapping.HasManyToMany<ServiceType>(x => x.ServiceTypes)
.Schema("Marketplace")
.Table("ListingServiceTypes")
.ParentKeyColumn("PackageID")
.ChildKeyColumn("ServiceTypeID")
.Cascade.SaveUpdate().LazyLoad();
Now since there is no entity to "test" this relationship with, I do not run over it.
What I need to know is how can I find the properties of an object that are mapped with "HasManyToMany". I can invoke them just fine, if I could only detect them.
I do not want to have to force lazy loading of every collection because if the mapping for the individual entities are correct, the mappings will be, because we use conventions for them.
Get the source code of FluentNHibernate, and copy to your project HasManyToManyStep.cs (FluentNHibernate.Automapping.Steps)
Add your logic to ShouldMap() method. This method is called by FNH to detect Many To Many relations. You can alter the way many to many relations are determined (for example by an attribute). In your case you want probably add a an attribute by reflection to tag the properties...
Replace the default step with your new one :
public class MyMappingConfiguration : DefaultAutomappingConfiguration
{
public override IEnumerable<IAutomappingStep> GetMappingSteps(AutoMapper mapper, IConventionFinder conventionFinder)
{
var steps = base.GetMappingSteps(mapper, conventionFinder);
var finalSteps = steps.Where(c => c.GetType() != typeof(FluentNHibernate.Automapping.Steps.HasManyToManyStep)).ToList();
var idx = finalSteps.IndexOf(steps.Where(c => c.GetType() == typeof(PropertyStep)).First());
finalSteps.Insert(idx + 1, new MyCustomHasManyStep(this));
return finalSteps;
}
}
Had to do this today.
var CollectionMetaData = SessionFactory.GetCollectionMetadata(T.FullName + '.' + info.Name);
if (CollectionMetaData is NHibernate.Persister.Collection.BasicCollectionPersister)
{
if (((NHibernate.Persister.Collection.BasicCollectionPersister)CollectionMetaData).IsManyToMany)
{
//Do something.
}
}
where T is the Type and info is the property info from T.GetProperties()

How to add mappings by namespace in Fluent NHibernate

In my application, I need to talk to multiple databases. I am handling this in NHibernate by creating one SessionFactory per database (I assume this is the correct thing to do). So I have two sets of models (one per database), and two sets of Fluent NHibernate ClassMap<> mappings. Both are in the same project (separated by namespace) and I'd like to keep it that way.
The problem comes when creating the SessionFactory. As far as I can see, Fluent NHibernate has basically two methods for adding mappings:
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserClassMap>())
.Mappings(m => m.FluentMappings.Add<UserClassMap>()
If I use the first overload, then my session factories get all the mappings for both databases. If I use the second, I have to specify each individual ClassMap. I'd like something like FluentMappings.AddFromNamespace(). Is there a way to do this?
It's odd that FluentNHibernate supports this type of filtering for automapping, but not for ClassMaps. It shouldn't be too hard to add this feature yourself, though, via the magic of extension methods. Try this:
public static FluentMappingsContainer AddFromAssemblyOf<T>(
this FluentMappingsContainer mappings,
Predicate<Type> where)
{
if (where == null)
return mappings.AddFromAssemblyOf<T>();
var mappingClasses = typeof(T).Assembly.GetExportedTypes()
.Where(x => (typeof(IMappingProvider).IsAssignableFrom(x)
|| typeof(IExternalComponentMappingProvider).IsAssignableFrom(x))
&& where(x));
foreach (var type in mappingClasses)
{
mappings.Add(type);
}
return mappings;
}
... and use it like this:
m.FluentMappings.AddFromAssemblyOf<UserClassMap>(t => t.Namespace.StartsWith("One.Of.The.Two.Namespaces"));
I wound up writing an extension method that does this for me. Basically it uses reflection to iterate over all the types I'm interested in, and add them one-by-one. It is based on the implementation of AddFromAssemblyOf. Usage:
.Mappings(m => m.FluentMappings.AddFromNamespaceOf<UserClassMap>())
Implementation:
public static class FluentNHibernateExtensions
{
public static FluentMappingsContainer AddFromNamespaceOf<T>(
this FluentMappingsContainer fmc)
{
string ns = typeof(T).Namespace;
IEnumerable<Type> types = typeof(T).Assembly.GetExportedTypes()
.Where(t => t.Namespace == ns)
.Where(x => IsMappingOf<IMappingProvider>(x) ||
IsMappingOf<IIndeterminateSubclassMappingProvider>(x) ||
IsMappingOf<IExternalComponentMappingProvider>(x) ||
IsMappingOf<IFilterDefinition>(x));
foreach(Type t in types) {
fmc.Add(t);
}
return fmc;
}
/// <summary>
/// Private helper method cribbed from FNH source (PersistenModel.cs:151)
/// </summary>
private static bool IsMappingOf<T>(Type type)
{
return !type.IsGenericType && typeof(T).IsAssignableFrom(type);
}
}
Caveats:
The name is a little misleading, since it only searches one assembly. It should perhaps be called AddFromAssemblyAndNamespaceOf, but that's a little verbose.
It is not entirely future-proof. If future versions of FNH add or remove some of the mappable interfaces, it wouldn't include them.
But it works for my purposes.
... AutoMap.AssemblyOf<Person>().Where(x => x.Namespace.EndsWith("Domain")) ...
There is no way to do this. I recommend separating the namespaces out into separate projects. Remember:
Separate namespaces, same project when logical separation makes sense.
Separate namespaces, separate projects when physical separation makes sense.
In this case, since you can't separate by namespace in nhibernate mappings, physical separation makes sense. You can, however, get around this with with fluent automaps that use a .Where or a ShouldMap configuration. Look up fluent automaps and see if that can get you where you want to be.

Using custom C# attributes to select Fluent conventions

Suppose you have sets of Fluent conventions that apply to specific groups of mappings, but not to all of them.
My thought here was, I'll create custom C# attributes that I can apply to the Fluent *Map classes - and write conventions that determine acceptance by inspecting the *Map class to see if the custom attribute was applied.
That way, I can select groups of conventions and apply them to various mappings by just tagging them with a custom attribute - [UseShortNamingConvention], etc.
I'm new to NHibernate (and Fluent, and C# for that matter) - is this approach possible?
And is it sane? :-)
Thanks!
Yes it is! Ive actually done something similiar but went with Marker Interfaces instead (INotCacheable, IComponent), but Marker Interface or Attribute, should't be that much of a difference.
When applying your conventions, just check for the presence of your attribute and ur good :)
EDIT:
Adding some code samples
public class MyMappingConventions : IReferenceConvention, IClassConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Key.Column(instance.EntityType.Name + "ID");
instance.LazyLoad();
instance.Inverse();
instance.Cascade.SaveUpdate();
if ((typeof(INotCacheable).IsAssignableFrom(instance.Relationship.Class.GetUnderlyingSystemType())))
return;
instance.Cache.ReadWrite();
}
public void Apply(IClassInstance instance)
{
instance.Table(instance.EntityType.Name + "s");
//If inheriting from IIMutable make it readonly
if ((typeof(IImmutable).IsAssignableFrom(instance.EntityType)))
instance.ReadOnly();
//If not cacheable
if ((typeof(INotCacheable).IsAssignableFrom(instance.EntityType)))
return;
instance.Cache.ReadWrite();
}
}