Would you help me to translate the above phrase ?
Or perhaps you want know something more ?
I can continue but frankly I have my ba…ehm.. sorry… I mean… I’m something tired of this situation.
Since the begin of FluentNHibernate the attitude was the same: say that NHibernate is a shit and you will be happy. Since the begin of FluentNHibernate we have asked a patch or something and the result was zero lines of code but each time I, or we (the team), do something, somebody come with the same aseptic critics without a single line of C# code. Which is the point ? What mean “Fluent NHibernate is smaller and more flexible than NHibernate” ? More than 600 *.cs files “only” to create a mapping is smaller than what ? an entirely ORM framework ? What is FNH without NHibernate ? bah?!??!?
I done ConfORM yes!! why ? I have asked to the team and the team said “if it will be not ready for 3.0 then please do it out from core” and in that time I was needing something to do a specific big work. Few days ago I have asked again and the team gave me its “OK!!”.
There are people arguing that ConfORM is the same of FluentNHibernate but with an ugly API. WOW!! THANKS!! you are saying that I done the same work, alone and in my free time within less than 3 months, with more flexibility and around 250 *.cs files instead more than 600. But are you really sure that I’m doing the same ? I think that ConfORM is more powerful than FNH but this is not the point. Each time I have published a post about ConfORM some FNH’s fanatics had the pleasure to ask me the same example but using FNH… ehi MAN!! do you haven’t something else to do ? FNH team ask me (not to the team but to me directly) that I have to implement a public API to let FNH avoid XMLs, FNH users ask to me to publish my examples about ConfORM but using FNH…when I implement another public API, to create mapping, again there is something else to do in another way… are you crazy or what ?
The discussion about the API, of explicit mapping, used in NHibernate is again another point where people like to say something. That API was proposed to the team and the team have discussed it (and this is not the first time I’m saying the same as you can read here and here and here if you follow all links in those posts). We “imitate” the XML simplifying it where possible. If a user know XML then he can understand and follow the new API. All existing documentation is reusable. Which was the proposal of some FNH’s fanatic ? “you have to use the API of FNH instead that ugly API”… What ? Which will be the end of FluentNHibernate project if NH includes that API ?
All decisions was and are discussed by the team because NH not only has a big community but even is not “one man show”; NHibernate is a team!! We are following MVCs (Most Valuable Contributors) in our JIRA and around the NET and each year we invite some MVCs to become committers. Because I really care about NHibernate, so much that I have more than 1400 commits only in the core, I’m pushing people to implement more and more options to map it. I’m asking myself the reason because some FluentNHibernate fanatics are so blind to don’t see the point.
After these few words its time to show you the code. This time I’ll not use words as “Map NHibernate using your API” or “Map NHibernate using your DSL” (a vague reference to the recipient of the message) but… well… you will see.
Mapping class-by-class
The target is, using the high-level API of NHibernate 3.2, implement what is needed to use this other final API:{
public EmployeeMap()
{
Id(x => x.Id);
Map(x => x.FirstName);
Map(x => x.LastName);
References(x => x.Store);
}
}
public class LocationMap : ComponentMap<Location>
{
public LocationMap()
{
Map(x => x.Aisle);
Map(x => x.Shelf);
}
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Price);
HasManyToMany(x => x.StoresStockedIn)
.Cascade.All()
.Inverse()
.Table("StoreProduct");
Component(x => x.Location);
}
}
public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasManyToMany(x => x.Products)
.Cascade.All()
.Table("StoreProduct");
HasMany(x => x.Staff)
.Cascade.All()
.Inverse();
}
}
All classes/interfaces needed to reproduce the Fluent-NHibernate’s API using the NHibernate 3.2 high-level API are:
{
private readonly ClassMapping<T> mapper = new ClassMapping<T>();
public IdentityPart<T, TProperty> Id<TProperty>(Expression<Func<T, TProperty>> idProperty)
{
return new IdentityPart<T, TProperty>(mapper, idProperty);
}
public PropertyBuilder<T, TProperty> Map<TProperty>(Expression<Func<T, TProperty>> property)
{
return new PropertyBuilder<T, TProperty>(mapper, property);
}
public ManyToOneBuilder<T, TOther> References<TOther>(Expression<Func<T, TOther>> property) where TOther : class
{
return new ManyToOneBuilder<T, TOther>(mapper, property);
}
public IOneToManyPart HasMany<TChild>(Expression<Func<T, IEnumerable<TChild>>> property)
{
// TODO: In FNH you have to find the way to know the type of the collection (Bag,Set,List etc.);
return new OneToManyPart<T, TChild, IBagPropertiesMapper<T, TChild>>(property, mapper.Bag);
}
public IManyToManyPart HasManyToMany<TChild>(Expression<Func<T, IEnumerable<TChild>>> property)
{
// TODO: In FNH you have to find the way to know the type of the collection (Bag,Set,List etc.);
return new ManyToManyPart<T, TChild, IBagPropertiesMapper<T, TChild>>(property, mapper.Bag);
}
public void Component<TComponent>(Expression<Func<T, TComponent>> property) where TComponent: class
{
// TODO: In FNH you have to do the others stuff you need to return the "fluent" interface
mapper.Component(property);
}
IConformistHoldersProvider IMappingProvider.GetNHibernateMapping()
{
return mapper;
}
}
public class ComponentMap<T> : IMappingProvider where T : class
{
private readonly ComponentMapping<T> mapper = new ComponentMapping<T>();
public PropertyBuilder<T, TProperty> Map<TProperty>(Expression<Func<T, TProperty>> property)
{
return new PropertyBuilder<T, TProperty>(mapper, property);
}
IConformistHoldersProvider IMappingProvider.GetNHibernateMapping()
{
return mapper;
}
}
public interface IMappingProvider
{
IConformistHoldersProvider GetNHibernateMapping();
}
public class IdentityPart<T, TProperty> where T : class
{
private readonly ClassMapping<T> mapper;
private readonly Expression<Func<T, TProperty>> idProperty;
public IdentityPart(ClassMapping<T> classMapping, Expression<Func<T, TProperty>> idProperty)
{
this.mapper = classMapping;
this.idProperty = idProperty;
mapper.Id(idProperty, x => { });
}
public IdentityPart<T, TProperty> Column(string columnName)
{
mapper.Id(idProperty, x => x.Column(columnName));
return this;
}
}
public class PropertyBuilder<T, TProperty> where T : class
{
private readonly IPropertyContainerMapper<T> mapper;
private readonly Expression<Func<T, TProperty>> property;
public PropertyBuilder(IPropertyContainerMapper<T> classMapping, Expression<Func<T, TProperty>> property)
{
this.mapper = classMapping;
this.property = property;
mapper.Property(property, x => { });
}
public PropertyBuilder<T, TProperty> Column(string columnName)
{
mapper.Property(property, x => x.Column(columnName));
return this;
}
}
public class ManyToOneBuilder<T, TOther>
where T : class
where TOther : class
{
private readonly ClassMapping<T> mapper;
private readonly Expression<Func<T, TOther>> property;
public ManyToOneBuilder(ClassMapping<T> classMapping, Expression<Func<T, TOther>> property)
{
this.mapper = classMapping;
this.property = property;
mapper.ManyToOne(property, x => { });
}
public ManyToOneBuilder<T, TOther> Column(string columnName)
{
mapper.ManyToOne(property, x => x.Column(columnName));
return this;
}
}
public interface ICollectionAttributesApplier<T, TChild> where T : class
{
void ApplyAttributes(Action<ICollectionPropertiesMapper<T, TChild>> apply);
}
public interface IOneToManyPart
{
ICollectionCascadeExpression<IOneToManyPart> Cascade { get; }
IOneToManyPart Inverse();
}
public class OneToManyPart<T, TChild, TCollectionCustomizer> : ICollectionAttributesApplier<T, TChild>, IOneToManyPart
where T : class
where TCollectionCustomizer : ICollectionPropertiesMapper<T, TChild>
{
private readonly Expression<Func<T, IEnumerable<TChild>>> property;
private readonly Action<Expression<Func<T, IEnumerable<TChild>>>, Action<TCollectionCustomizer>, Action<ICollectionElementRelation<TChild>>> customizer;
public OneToManyPart(Expression<Func<T, IEnumerable<TChild>>> property,
Action<Expression<Func<T, IEnumerable<TChild>>>, Action<TCollectionCustomizer>, Action<ICollectionElementRelation<TChild>>> customizer)
{
this.property = property;
this.customizer = customizer;
ApplyRelation(x => x.OneToMany());
}
public ICollectionCascadeExpression<IOneToManyPart> Cascade
{
get { return new CollectionCascadeExpression<IOneToManyPart, T, TChild, OneToManyPart<T, TChild, TCollectionCustomizer>>(this, this); }
}
public IOneToManyPart Inverse()
{
ApplyAttributes(x => x.Inverse(true));
return this;
}
public void ApplyAttributes(Action<ICollectionPropertiesMapper<T, TChild>> apply)
{
customizer(property, att => apply(att), rel => { });
}
private void ApplyRelation(Action<ICollectionElementRelation<TChild>> apply)
{
customizer(property, x => { }, apply);
}
}
public interface IManyToManyPart
{
ICollectionCascadeExpression<IManyToManyPart> Cascade { get; }
IManyToManyPart Table(string tableName);
IManyToManyPart Inverse();
}
public class ManyToManyPart<T, TChild, TCollectionCustomizer> : ICollectionAttributesApplier<T, TChild>, IManyToManyPart
where T : class
where TCollectionCustomizer : ICollectionPropertiesMapper<T, TChild>
{
private readonly Expression<Func<T, IEnumerable<TChild>>> property;
private readonly Action<Expression<Func<T, IEnumerable<TChild>>>, Action<TCollectionCustomizer>, Action<ICollectionElementRelation<TChild>>> customizer;
public ManyToManyPart(Expression<Func<T, IEnumerable<TChild>>> property,
Action<Expression<Func<T, IEnumerable<TChild>>>, Action<TCollectionCustomizer>, Action<ICollectionElementRelation<TChild>>> customizer)
{
this.property = property;
this.customizer = customizer;
ApplyRelation(x => x.OneToMany());
}
public ICollectionCascadeExpression<IManyToManyPart> Cascade
{
get { return new CollectionCascadeExpression<IManyToManyPart, T, TChild, ManyToManyPart<T, TChild, TCollectionCustomizer>>(this, this); }
}
public IManyToManyPart Inverse()
{
ApplyAttributes(x => x.Inverse(true));
return this;
}
public IManyToManyPart Table(string tableName)
{
ApplyAttributes(x => x.Table(tableName));
return this;
}
public void ApplyAttributes(Action<ICollectionPropertiesMapper<T, TChild>> apply)
{
customizer(property, att => apply(att), rel => { });
}
private void ApplyRelation(Action<ICollectionElementRelation<TChild>> apply)
{
customizer(property, x => { }, apply);
}
}
public interface ICollectionCascadeExpression<TParent>
{
TParent All();
TParent DeleteOrphan();
TParent AllDeleteOrphan();
}
public class CollectionCascadeExpression<TParent, TContainer, TChild, TApplier> : ICollectionCascadeExpression<TParent>
where TParent : class
where TContainer : class
where TApplier : ICollectionAttributesApplier<TContainer, TChild>
{
private readonly TParent parent;
private readonly TApplier applier;
public CollectionCascadeExpression(TParent parent, TApplier applier)
{
this.parent = parent;
this.applier = applier;
}
public TParent All()
{
applier.ApplyAttributes(x => x.Cascade(Cascade.All));
return parent;
}
public TParent DeleteOrphan()
{
applier.ApplyAttributes(x => x.Cascade(Cascade.DeleteOrphans));
return parent;
}
public TParent AllDeleteOrphan()
{
applier.ApplyAttributes(x => x.Cascade(Cascade.All.Include(Cascade.DeleteOrphans)));
return parent;
}
}
The code used for the method to build the SessionFactory is:
{
var configure = new Configuration();
configure.DataBaseIntegration(x =>
{
x.Dialect<MsSql2008Dialect>();
x.ConnectionString = (new SqlConnectionStringBuilder
{
DataSource = @"localhost\SQLEXPRESS",
InitialCatalog = "reFNH",
IntegratedSecurity = true
}).ToString();
x.SchemaAction = SchemaAutoAction.Recreate;
});
return configure.AddMappingsFromAssemblyOf<Program>().BuildSessionFactory();
}
The extension method to add all mappings using the new implementations is:
{
var mapper = new ModelMapper();
mapper.BeforeMapClass += (mi, t, cam) => cam.Id(x => x.Generator(Generators.Native));
foreach (Type type in typeof(T).Assembly.GetExportedTypes().Where(x => typeof(IMappingProvider).IsAssignableFrom(x) && !x.IsGenericTypeDefinition && !x.IsInterface))
{
IMappingProvider mappingInstance;
try
{
mappingInstance = (IMappingProvider)Activator.CreateInstance(type);
}
catch (Exception e)
{
throw new MappingException("Unable to instantiate mapping class (see InnerException): " + type, e);
}
mapper.AddMapping(mappingInstance.GetNHibernateMapping());
}
conf.AddDeserializedMapping(mapper.CompileMappingForAllExplicitAddedEntities(), "Domain");
return conf;
}
Conclusion
Now not only you have the API you was asking for 3 years (implemented as we need) but you have even the starting point to use it in Fluent-NHibernate and put the turbo to your framework. If you want/need you can use a more low-level API; the name space where look is NHibernate.Mapping.ByCode.Impl .If you need something else, please let me know.P.S. About “”auto”” mapping I’ll be back soon.