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()
Related
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.
I created a generic user repository base class that provides reusable user management functionality.
public class UserRepository<TUser> where TUser : new, IUser
{
}
I have a concrete implementation of IUser called UserImpl, and corresponding mapping class UserImplMap : ClassMap<UserImpl> (they all are in the same namespace and assembly). I add the mapping using AddFromAssemblyOf . I also use this to create / generate the schema.
So far so good and things work as expected.
Now, in a different project, I needed a few additional properties in my IUser implementation class, so I implemented a new class UserImplEx : UserImpl. This class has the additional properties that I needed. Also, I created a new mapping class UserImplExMap : SubclassMap<UserImplEx>
Now when I create schema using this approach, I get two tables one for UserImpl and one for UserImplEx.
Is is possible to configure / code Fluent mapping in some way so that all the properties (self, plus inherited) of UserImplEx get mapped in a single table UserImplEx instead of getting split into two tables?
Alternatively, if I provide full mapping in UserImplExMap : ClassMap<UserImplEx>, then I do get the schema as desired, but I also get an additional table for UserImpl (because corresponding mapping is present in the UserRepository assembly). If I follow this approach, is there a way to tell AddFromAssemblyOf to exclude specific mapping classes?
Option 1
since you have inhertance here and want the correct type back NH has to store the type somewhere, either through the table the data is in or a discriminator.
If a discriminator column in the table does not matter then add DiscriminatorColumn("userType", "user"); in UserImplMap and DiscriminatorValue("userEx") in UserImplExMap
Option 2
class MyTypeSource : ITypeSource
{
private ITypeSource _inner = new AssemblyTypeSource(typeof(UserImplMap).Assembly);
public IEnumerable<Type> GetTypes()
{
return _inner.Where(t => t != typeof(UserImplMap)).Concat(new [] { typeof(UserImplExMap) });
}
public void LogSource(IDiagnosticLogger logger)
{
_inner.LogSource(logger);
}
public string GetIdentifier()
{
return _inner.GetIdentifier();
}
}
and when configuring
.Mappings(m =>
{
var model = new PersistenceModel();
PersistenceModel.AddMappingsFromSource(new MyTypeSource());
m.UsePersistenceModel(model);
})
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
}
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.
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.