Using Temporary Objects causes Transient Object Exception
I have an object where I maintain a relationship to another object:
public class Foo {
public Bar Bar { get; set; }
}
In the mapping I reference Bar to that I can maintain the relationship - I don't want the parent object to update properties in the child, just the existence of the relationship:
References(x => x.Bar, "BarId")
.Cascade.None();
In the UI layer I create the relationship using a property which is not the underlying primary key:
item.Bar = new Bar { Code = "123" };
In the repository layer I hydrate the object if it doesn't have the primary key populated:
if(item.Bar.Id == null)
{
item.Bar = barRepository.RetrieveByCode(item.Bar.Code);
}
When I the RetrieveByCode line runs (开发者_如何学Gowhich is a Criteria.UniqueResult under the covers) I get a TransientObjectException telling me that "the object references an unsaved transient instance - save the transient instance before flushing" for the Bar type.
When I run the same code path without creating the temporary Bar object it works. It appears that the Bar created as a temporary oject is tracked by NHibernate, yet I want it to forget that it ever existed as it is only a placeholder.
Any thoughts on how to achieve this?
UPDATE: Doing some more testing on this it seems to be the change tracking in Foo that is causing trouble. If I call Session.Evict(item) after retrieving it, but before making any changes and then re-attach the object using Session.Update(item) after I am done it seems to work, however it updates the child objects which is not what I want - I only want to manage the relationship.
UPDATE 2: I changed the FlushMode from Auto to Commit. It seems to have disabled the queueing of any interim changes to the object. Having researched NH behavior a bit further it seems that Update works more like a "re-attach" call rather than an explicit "update now" call.
UPDATE 3: It appears changing FlushMode caused other issues with transactions that required several operational steps. I reverted back to try another approach:
if(item.Bar.Id == null)
{
var barCode = item.Bar.Code;
item.Bar = null;
item.Bar = barRepository.RetrieveByCode(barCode);
}
Why do you want it to work that way? Why not simply set item.Bar using the retrieved Bar object:
item.Bar = barRepository.RetrieveByCode("123");
You might be able to make your current pattern work using Load
:
if(item.Bar.Id == null)
{
var bar = barRepository.RetrieveByCode(item.Bar.Code);
item.Bar = session.Load<Bar>(bar.Id);
}
精彩评论