Nhibernate Migration from 1.0.2.0 to 2.1.2 and many-to-one save problems
we have an old, big asp.net application with nhibernate, which we are extending and upgrading some parts of it. NHibernate that was used was pretty old ( 1.0.2.0), so we decided to upgrade to ( 2.1.2) for the new features. HBM files are generated through custom template with MyGeneration. Everything went quite smoothly, except for one thing.
Lets say we have to objects Blog and Post. Blog can have many pos开发者_开发知识库ts, so Post will have many-to-one relationship. Due to the way that this application operates, relationship is done not through primary keys, but through Blog.Reference column.
Sample mapings and .cs files:
<?xml version="1.0" encoding="utf-8" ?>
<id name="Id" column="Id" type="Guid">
<generator class="assigned"/>
</id>
<property column="Reference" type="Int32" name="Reference" not-null="true" />
<property column="Name" type="String" name="Name" length="250" />
</class>
<?xml version="1.0" encoding="utf-8" ?>
<id name="Id" column="Id" type="Guid">
<generator class="assigned"/>
</id>
<property column="Reference" type="Int32" name="Reference" not-null="true" />
<property column="Name" type="String" name="Name" length="250" />
<many-to-one name="Blog" column="BlogId" class="SampleNamespace.BlogEntity,SampleNamespace" property-ref="Reference" />
</class>
And class files
class BlogEntity
{
public Guid Id { get; set; }
public int Reference { get; set; }
public string Name { get; set; }
}
class PostEntity
{
public Guid Id { get; set; }
public int Reference { get; set; }
public string Name { get; set; }
public BlogEntity Blog { get; set; }
}
Now lets say that i have a Blog with Id 1D270C7B-090D-47E2-8CC5-A3D145838D9C and with Reference 1
In old nhibernate such thing was possible:
//this Blog already exists in database
BlogEntity blog = new BlogEntity();
blog.Id = Guid.Empty;
blog.Reference = 1; //Reference is unique, so we can distinguish Blog by this field
blog.Name = "My blog";
//this is new Post, that we are trying to insert
PostEntity post = new PostEntity();
post.Id = Guid.NewGuid();
post.Name = "New post";
post.Reference = 1234;
post.Blog = blog;
session.Save(post);
However, in new version, i get an exception that cannot insert NULL into Post.BlogId. As i understand, in old version, for nhibernate it was enough to have Blog.Reference field, and it could retrieve entity by that field, and attach it to PostEntity, and when saving PostEntity, everything would work correctly. And as i understand, new NHibernate tries only to retrieve by Blog.Id.
How to solve this? I cannot change DB design, nor can i assign an Id to BlogEntity, as objects are out of my control (they come prefilled as generic "ojbects" like this from external source)
It seems very strange to me that the code worked in NH 1. But, since it is not working at the moment anyway, I think you have to look for the blog entity in a query first:
var criteria = DetachedCriteria.For<Blog>();
criteria.Add(Expression.Eq("Reference", 1));
var blog = criteria.GetExecutableCriteria(session).List<Blog>().FirstOrDefault();
post.Blog = blog;
session.Save(post);
this
blog.Id = Guid.Empty
is translated as a null in the DB. So when you change it (as the sample code implies) you are explicitly setting a null value on the BlogEntity Id.
This is the error you are receiving and is irrelevant of the "Reference" column/property.
As for the question of what you can do... well you don't have to make the ORM joins on the Guids! You can make the joins on the Reference column...
Answering my own question.
The problem was that nhibernate was hiting DB to retrieve BlogEntity with id 00000000-0000-0000-0000-000000000000. Of course in DB it got nothing, so it tried to insert null
And it was clearly visible in logs why it was happening
Unable to determine if BlogEntity with assigned identifier 00000000-0000-0000-0000-000000000000 is transient or detached; querying the database. Use explicit Save() or Update() in session to prevent this.
Solved it my implementing IInterceptor
, passing it to Session and especially its method bool? IsTransient(object entity)
And problem solved.
精彩评论