在我的對象圖中,Person
與Address
有多對多的關係,並且連接表有附加列。使用Cascade.All時出現的TransientObjectException
階層結構
class Person
{
private IList<PersonAddress> _personAddresses = new List<PersonAddress>();
public virtual int Id { get; set; }
public virtual IList<PersonAddress> PersonAddresses
{
get { return _personAddresses; }
set { _personAddresses = value; }
}
}
class PersonAddress
{
public virtual Person Person { get; set; }
public virtual Address Address { get; set; }
public virtual string Description { get; set; }
public override bool Equals(...) {...}
public override int GetHashCode(...) {...}
}
class Address
{
public virtual int Id { get; set; }
}
映射
class PersonMapping : ClassMapping<Person>
{
public PersonMapping()
{
Id(x => x.ID, m => m.Generator(Generators.Identity));
Bag(
x => x.PersonAddresses,
m => {
m.Cascade(Cascade.All);
m.Access(Accessor.Field);
},
r => r.OneToMany()
);
}
}
public class PersonAddressMapping : ClassMapping<PersonAddress>
{
public PersonAddressMapping()
{
ComposedId(map =>
{
map.ManyToOne(
x => x.Person,
m => {
m.Cascade(Cascade.All);
}
);
map.ManyToOne(
x => x.Address,
m => {
m.Cascade(Cascade.All);
}
);
map.Property(x => x.Description);
});
}
}
public class AddressMapping : ClassMapping<Address>
{
public AddressMapping()
{
Id(x => x.ID, m => m.Generator(Generators.Identity));
}
}
用法
using (var session = sessionFactory.OpenSession())
using (var transaction = session.BeginTransaction())
{
var person = new Person();
var address = new Address();
var personAddress = new PersonAddress
{
Address = address,
Person = person,
Description = "This is my home address"
};
person.PersonAddresses.Add(personAddress);
session.Save(person);
// exception of NHibernate.TransientObjectException
transaction.Commit();
}
異常
object references an unsaved transient instance -
save the transient instance before flushing or set
cascade action for the property to something that
would make it autosave.
Type: MyApp.Models.Address, Entity: MyApp.Models.Address
我相信我上面的代碼不應該是有問題的,因爲我節省了Person
,該級聯下到PersonAddress
,然後落下下來到Address
。然而,NHibernate告訴我要麼自動保存(與級聯?),要麼自己保存它。
解決方法
session.Save(person);
session.Save(address);
transaction.Commit();
然而,如實際生產代碼比短例子複雜得多,這是非常有問題的。在實際生產代碼中,我有一個Organization
對象,其中包含一個Person
(其中包含人員地址和地址)的列表。
有沒有辦法解決這個問題,而不必附加Save
呼叫破解,因爲它是很難寫在一個通用的方法,同時嘗試我的應用程序邏輯和持久性邏輯分離的方式。
爲什麼解決辦法不會對我的方案
// where unitOfWork is a wrapper for the session
using (var unitOfWork = unitOfWorkFactory.Create())
{
var organization = unitOfWork.OrganizationRepository.GetById(24151);
organization.AddPerson(new Person {
PersonAddress = new PersonAddress {
Address = new Address(),
Description = "Some description"
}
});
unitOfWork.Commit();
}
正如你所看到的,UnitOfWork
,UnitOfWorkFactory
工作,OrganizationRepository
都是抽象的,因此不可能爲我節省地址和人而不會泄露那些實現細節,如果持久性如我所料地下降,我認爲我應該能夠做到這一點。
我的問題是,如何堅持Address
沒有明確告訴NHibernate這樣做?
這可能不使用代理鍵完成? – Matthew
不能,我確定:(對不起,你仍然可以調用session.Save(地址)...然後確定,但是在這種情況下你更喜歡...不,Cascade不能在複合主鍵插入時工作。在這種情況下 –