Recently I've posted about my first impressions about NHibernate's mapping-by-code and the one thing I was surprised was that the default naming conventions provided are not usable.
I was using default Fluent NHibernate's naming convention for quite a long time and there were only few scenarios (not counting legacy databases) when I needed to specify columns or table names on my own. With mapping-by-code it is not possible to map simple many-to-many without changing the default naming convention or specifying the names in the mapping, quite XML-style. A bit annoying.
So, as a part of my experiments with the tool, I've written another naming convention that is aimed to resemble Fluent NHibernate's default naming convention, so that:
- foreign key columns in child-parent relationships are called "Parent_id"
- many-to-many intermediate table is called "FirstToSecond" and its foreign key columns are "First_id" and "Second_id"
- on bidirectional relations, both sides match together automatically
Nothing really impressing, but missing in default mapping-by-code naming convention.
The code is here on GitHub. Feel free to use it if you find it useful. It is of course far from being complete - it's not even trying to cope with features like maps, one-to-one's, any's, many-to-any's etc.
My convention doesn't support join either and it seems that it is not possible with current mapping-by-code implementation - there's no event to attach to in case of join mapping and it looks like joins are implemented in different way. Foreign key column of joined table is called "parent_key", and can be overriden in ClassMapping only.
I'm inheriting ConventionModelMapper behaviors and adding few own rules, that are applied before mapping, so that it can be overriden in ClassMappings in a standard way. I've used few extension methods provided by NHibernate's code. It's a pity that they are not mentioned anywhere - I've just found it in the source code quite accidentally.
When using my ModelMapperWithNamingConventions in place of ModelMapper or ConventionModelMapper, many-to-many verbose mapping I've presented previously is reduced to something more friendly and quite elegant, like Fluent NHibernate has accustomed me - I can remove key column definitions from both Bag and ManyToMany mapping and remove Table name definition from Bag mapping.
public class Street
{
public Street()
{
Districts = new List<District>();
}
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual ICollection<District> Districts { get; set; }
}
public class StreetMap : ClassMapping<Street>
{
public StreetMap()
{
Id(x => x.Id, m => m.Generator(Generators.Identity));
Property(x => x.Name, m => m.Length(SqlClientDriver.MaxSizeForLengthLimitedString + 1));
Bag(x => x.Districts, c =>
{
c.Key(k => k.NotNullable(true));
c.Cascade(Cascade.All);
c.Inverse(true);
}, r => r.ManyToMany());
}
}
And here are the tables generated by Hbm2dll tool:
Hi,
ReplyDeleteNice addition to the excellent mapping-by-code, but your implementation of the IsPersistentProperty delegate causes it to incorrectly attempt mapping of properties where the get or set is missing, i.e. calculated properties. To me, a better solution was this:
IsPersistentProperty((m, d) =>
{
var propertyInfo = m as PropertyInfo;
bool rv = true;
if (propertyInfo != null)
// Re-instate convention that we only map
// to database if property is read/write
rv = (propertyInfo.CanRead && propertyInfo.CanWrite);
rv = rv && !_ignoredMembers.Contains(m);
return rv;
});
/David
Thank you David for an excellent addition to the class. And of course a big thank you to the original author.
DeleteThe only thing I would add is the default configuration for Id property to be native.
ReplyDeleteI am trying to get an entirely convention based mapping going so I only need to define classmaps for special custom cases. I was doing OK until I tried to do Many to many's. I specified a lambda for IsManyToMany and BeforeMaps for Set and ManyToMany however it does not seem to work - http://stackoverflow.com/questions/13330554/nhibernate-bycode-mapping-how-to-map-manytomany-entirely-by-convention.
ReplyDeleteI seems I still must define a class mapping and Set for the property. I can leave everything out of the set definition except the relationship definition which MUST specify the column (same as what the BeforeManyToMany event is doing). If I dont it is treated like a OneToMany. So it's quirky... and seems like a bug. Last theory I have is to specify more of the IsXXX methods, at least for the other collection types, perhaps there is some ambiguity there i need to resolve.
This comment has been removed by a blog administrator.
ReplyDelete