使用Cascade.All时出现TransientObjectException [英] TransientObjectException when using Cascade.All

查看:118
本文介绍了使用Cascade.All时出现TransientObjectException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的对象图中,PersonAddress具有多对多关系,并且联接表具有其他列.

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();
}

如您所见,UnitOfWorkUnitOfWorkFactoryOrganizationRepository都是抽象,因此,对于我来说,在不泄漏实现细节的情况下保存地址和人员都是不可能的,我认为我应该如果持久性如我所料地级联,便可以做到.

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?

推荐答案

您所有的东西都可以使用...除非PersonAddress的映射不代表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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆