使用Cascade.All时出现TransientObjectException [英] TransientObjectException when using Cascade.All
问题描述
在我的对象图中,Person
与Address
具有多对多关系,并且联接表具有其他列.
In my object graph, a Person
has a many-to-many relationship with Address
, and the join table has additional columns.
类结构
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告诉我要么自动保存它(使用层叠?),要么自己保存它.
I believe that my above code should not be problematic, as I'm saving a Person
, which cascades down to the PersonAddress
, which then cascades down to the Address
. However, NHibernate is telling me to either autosave it (with cascade?), or to save it myself.
解决方法
session.Save(person);
session.Save(address);
transaction.Commit();
但是,这是非常有问题的,因为实际的生产代码比简短的示例要复杂得多.在实际的生产代码中,我有一个Organization
对象,其中包含一个Person
列表(然后它具有人员地址和地址).
However, this is very problematic as the actual production code is much more complex than the short example. In the actual production code, I have an Organization
object which contains a list of Person
(which then has personaddresses, and addresses).
有没有一种方法可以解决此问题而不必进行额外的Save
调用,因为在尝试将应用程序逻辑与持久性逻辑分开时,很难以通用的方式编写该代码.
Is there a way to solve this problem without having to hack in an additional Save
call, as it's difficult to write that in a generic way while try to separate my application logic from the persistence logic.
为什么该变通办法不适用于我的情况
// 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
都是抽象,因此,对于我来说,在不泄漏实现细节的情况下保存地址和人员都是不可能的,我认为我应该如果持久性如我所料地级联,便可以做到.
As you can see, the UnitOfWork
, UnitOfWorkFactory
, and OrganizationRepository
are all abstractions, and therefore would be impossible for me to save both address and person without leaking that implementation detail, which I think I should be able to do if the persistence cascaded as I expected.
我的问题是,如何在不明确告诉NHibernate的情况下坚持Address
?
My question is, how do I persist Address
without explicitly telling NHibernate to do so?
推荐答案
您所有的东西都可以使用...除非Person
和Address
的映射不代表composite-id
.
All your stuff would work ... unless the mapping of the Person
and Address
won't be representing the composite-id
.
尽管如此,您仍然可以在CompositeId
映射内使用Cascade.All
Despite fo the fact, that you could use Cascade.All
inside of the CompositeId
mapping
ComposedId(map =>
{
map.ManyToOne( x => x.Person,
m => { m.Cascade(Cascade.All); // Cascade here is not applied
这将不适用. <composite-id>
(文档5.1.5) 子元素<key-many-to-one>
不支持级联.
this won't be applied. The <composite-id>
(doc 5.1.5) sub-element <key-many-to-one>
does not support cascading.
但,如果PersonAddress
具有一些替代密钥,所有内容都将起作用,并且对 Person 和 Adress 的引用将起作用被cascade="all"
BUT, all the stuff would work, if the PersonAddress
would have some surrogated key, and references to Person and Adress will be mapped as standard many-to-one
with cascade="all"
也可以在此处查看答案 NHibernate-如何将带有父子引用的Composite-id映射 ...以获得更多原因使用替代的而非组合的ID
Also see answers here NHibernate - How to map composite-id with parent child reference ... to get more reasons to use surrogated, not composite id
这篇关于使用Cascade.All时出现TransientObjectException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!