Saturday, August 14, 2010

Understanding Fluent NHibernate - The entity doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id)

The entity 'YourEntityName' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id). Don't you just love Fluent NHibernate error messages?

So you have set up your database, got your tables together, created some classes and tried to use Fluent NHibernate to map and......you get the following - The entity 'YourEntityName' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id). It seems like a straightforward error right? It looks like Fluent NHibernate is looking for an Id and you don't have it. But you think to yourself, "Self...you know you put that YourPropertyNameId in there. What could be going on?"

Well the truth is that the error is not as verbose as it seems. It means EXACTLY what it says. Let us say for a moment that you have a table named Projects. This Projects table has a primary key column called ProjectId. You are using Fluent NHibernate and NHibernate in ASP.NET MVC to automagically generate NHibernate mappings so that you don't have to do it by hand. Well, Fluent NHibernate expects to see that primary key column name displayed as Id NOT as ProjectId! What???? That is correct - Fluent NHibernate can't be expected to just "know" what everyone using it will name the primary key - soooo when you are using the AUTOMAPPING FEATURE that allows you to generate mapping files WITHOUT typing a line of code, you have to give those identity columns a nice GENERIC name of Id. Using something like {get; private set;} is not enough. If that naming convention doesn't work for you, you can do a few things. You can override the IsId feature. The override below, which would be placed in the configuration file, specifies that anything with the EntityNameId is the Id.


public override bool IsId(Member member)
{
return member.Name == member.DeclaringType.Name + "Id";
}


Now, that is the way to do it IF you are using configuration files. I don't.
I should because that was the suggested upgrade from Fluent NHibernate 1.0 to 1.1,
but I don't because the creator in his infinite wisdom still allows me to do it like this.



public ISessionFactory GetSessionFactory()
{
var connectionString = ConfigurationManager
.ConnectionStrings["YOUR_CONNECTION
_STRING_NAME_AS_DEFINED_
IN_YOUR_WEB_CONFIG_FILE
"]
.ConnectionString;

//Fluent NHibernate configurartion
var sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005
.ConnectionString(x => x
.Is(connectionString)))
.Mappings(m => m.AutoMappings
.Add(AutoMap.AssemblyOf<YOUR_ENTITY_NAME>()
.Setup(cfg =>
{cfg.FindIdentity = member => member
.Name == member
.DeclaringType.Name + "Id";}
)))
.BuildSessionFactory();

return sessionFactory;
}


For
YOUR_ENTITY_NAME shown above, you can select ANY class in the directory that you want. Fluent NHibernate just uses that class to tell where ALL of your classes are so don't worry about which one to specify there. You can also use Fluent NHibernate to generate the actual mapping classes decorated with ClassMap and then make any adjustments . However, that means that you can't use the Automapping magical save-me-some-work feature.

Fluent NHibernate can make your life sooo much easier if you are using NHibernate, but you have to read the automapping area on the Fluent NHibernate Wiki if you really want to be able to use it to its fullest potential. READ IT HERE :-). No one likes to spend precious minutes/hours looking for why you get small issues like this, but if you read the wiki maybe you won't have to. I promise you that it won't hurt!

Smooches,
Kila

Problem:
When using Fluent NHibernate with the AutoMap feature, you get the following error - The entity 'YourEntityName' doesn't have an Id mapped. Use the Id method to map your identity property.

Solution:
Rename your primary key from whatever it was to Id or create the ClassMap files by hand and designate the column to be used as your primary key in that class. Don't forget to change your Fluent.Configure .Mappings area to something like this - .Mappings(x => x.FluentMappings.AddFromAssemblyOf() - so that you remove the Automap from the Mappings attribute.

You can also designate your column as a primary key by using the hbm.xml file structure, but that is too much work and you don't have type checking to make sure you didn't screw something up AND you defeat the purpose of Fluent NHibernate. Don't defeat the purpose people - just don't do it!

4 comments:

Marco Developper said...

Id(x => x.ID).GeneratedBy.GetGeneratorMapping().IsSpecified("ID");

Daaaahhhhhh.... Women's

Marco Developper said...

Sorry babe...

Id(x => x.KeyName_ID).GeneratedBy.GetGeneratorMapping().IsSpecified("KeyName_ID");

Kila Morton said...

Marco 1 & 2 you DO REALIZE that I posted the AutoMap way right? My way is completely correct if you are using AutoMap. YOUR way is completely correct if you are NOT using AutoMap - so.....as you put it Marco #1 men.....

Marco Developper said...

Well Well Well....

Again ..

"Understanding Fluent NHibernate - The entity doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id)"

Thats the Title dear Kila :)

But you are completely right...

However, i must report one error in my code...


The solution for FluentMapping IS:

Id(x=>x."KeyName_Id").Column("KeyName_ID").Sequence("KeyName_Id_Seq")

or
[Bad policy but working]
Id(x=>x."KeyName_Id").Column("KeyName_ID").Assigned()

The firs solution that i posted fails on Insertion

So...
Dear Friends...
Remember...

Peace...
:)

PS: Kila thankx for the response ;)