Okay, so yesterday I managed to get the latest trunk builds of NHibernate and FluentNHibernate to work with my latest little project. (I'm working on a bug tracking application.) I created a nice data access layer using the Repository pattern.
I decided that my entities are nothing special, and also that with the current maturity of ORMs, I don't want to hand-craft the database.
So, I chose to use FluentNHibernate's auto mapping feature with NHibernate's "hbm2ddl.auto" property set to "create".
It really works like a charm. I put the NHibernate configuration in my app domain's config file, set it up, and started playing with it. (For the time being, I created some unit tests only.) It created all tables in the database, and everything I need for it. It even mapped my many-to-many relationships correctly.
However, there are a few small glitches:
- All of the columns created in the DB allow null. I understand that it can't predict which properties should allow null and which shouldn't, but at least I'd like to tell it that it should allow null only for those types for which null makes sense in .NET (eg. non-nullable value types shouldn't allow null).
- All of the nvarchar and varbinary columns it created, have a default length of 255. I would prefer to have them on max instead of that.
Is there a way to tell the auto mapper about the two simple rules above?
If the answer is no, will it work correctly if I modify the tables it created?
(So, if I set some columns not to allow null, and change the allowed length for some other, will it correctly work with them?)
FINAL EDIT:
Big Thanks to everyone who dropped by and helped out.
All of my issues with Fluent are solved now.
You can use Auto Mapping Overrides to change how the Auto Mapper work, and you can also define Conventions, that will be used instead by the auto mapper.
Here is an example on how to use both the conventions and the overrides:
var mappings = new AutoPersistenceModel();
mappings.Conventions.Setup(s => s.Add<ColumnNullabilityConvention>());
mappings.UseOverridesFromAssemblyOf<AssemblyName>();
// This convention will set all properties to be not nullable
public class ColumnNullabilityConvention: IPropertyConvention, IPropertyConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Nullable, Is.Not.Set);
}
public void Apply(IPropertyInstance instance)
{
instance.Not.Nullable();
}
}
// This override will change "string" to use "text" instead of "varchar(255)".
// Also set the property to be not nullable
public class SomeOverrideInTheSameAssembly : IAutoMappingOverride<TypeName>
{
public void Override(AutoMapping<TypeName> mapping)
{
mapping.Map(x => x.Property).CustomType("StringClob").CustomSqlType("text");
mapping.Map(x => x.Property).Not.Nullable();
}
}
Check these links for more examples:
- How to setup conventions and overrides
- Some convention overrides
- Fluent NHibernate Conventions FAQ
- Another SO link about this topic
For your Id woes, you need to change the FindIdentity
setting. It's covered in the automapping wiki page, although albeit briefly.
It should go something like this:
AutoMap.AssemblyOf<Entity>() // your usual setup
.Setup(s =>
{
s.FindIdentity = m => m.Name == "ID";
});
What this does is instruct the automapper to use your new lambda (m => m.Name == "ID"
) when trying to discover Ids. m
is the property/member, and this lambda is called for each property on each entity; whichever you return true for is the id.
It's not widely known, but you can set many conventions from the Mappings section in your Configure code e.g.
Fluently.Configure()
.Database(/* database config */)
.Mappings(m =>
{
m.FluentMappings
.AddFromAssemblyOf<Entity>()
.Conventions.Add(PrimaryKey.Name.Is(x => "ID"));
})
to set a Primary Key convention.
Edit: Clarification of what the PrimaryKey convention does:
The PrimaryKey convention is used to
specify what the column of the
primary key is, not the property.
Discovering the property is a pure
automapping exercise, while
conventions are applied to ClassMaps
and automappings. – James Gregory
This is the list of supported conventions (from the wiki):
Table.Is(x => x.EntityType.Name + "Table")
PrimaryKey.Name.Is(x => "ID")
AutoImport.Never()
DefaultAccess.Field()
DefaultCascade.All()
DefaultLazy.Always()
DynamicInsert.AlwaysTrue()
DynamicUpdate.AlwaysTrue()
OptimisticLock.Is(x => x.Dirty())
Cache.Is(x => x.AsReadOnly())
ForeignKey.EndsWith("ID")
See The Simplest Conventions section on the FNH wiki.