NHibernation:级联不更新子对象

时间:2010-11-08 20:44:03

标签: c# nhibernate fluent-nhibernate

好的,这将是一个文本墙,但......基本上,我有以下3个类:

public class Address
{
    public virtual int Id { get; private set; }
    public virtual string Street { get; set; }
    public virtual string POBox { get; set; }
    public virtual string ZIP { get; set; }
    public virtual string Locality { get; set; }
    public virtual Country Country { get; set; }
    public virtual ICollection<Customer> CustomersLivingHere { get; set; }
}


public class Customer
{
    public virtual int Id { get; private set; }

    public virtual string Firstname { get; set; }    
    public virtual string Lastname { get; set; }

    public virtual DateTime Birthdate { get; set; }

    public virtual ICollection<Address> Addresses { get; set; }
}

public class Country
{
    public virtual int Id { get; private set; }
    public virtual string Name { get; set; }
    public virtual string Domain { get; set; }
}

这是某种地址簿(在现实世界中显然有点复杂,但我已将单元测试减少到这个非工作情况)。

地址使用第三方提供的Web服务进行验证和规范化,理想情况下,我希望客户和地址之间的关系是多对多的,因此数据库中的每个地址都需要只能验证一次(我们为验证服务付钱),同时仍然与任意数量的客户相关联。

与此同时,我希望NHibernate在将地址分配给客户对象后自动存储地址并保存,但到目前为止,这根本不起作用。

这是我的映射文件查找Customer类的方式:

public class CustomerMap : FluentNHibernate.Mapping.ClassMap<Customer>
{
    public CustomerMap()
    {
        Id(x => x.Id);

        Map(x => x.Firstname).Length(256);
        Map(x => x.Lastname).Length(256);
        Map(x => x.Birthdate);

        HasMany(x => x.Addresses)
            .AsSet()
            .Inverse()
            .Cascade.All();
    }
}

...这是地址映射:

public class AddressMap : FluentNHibernate.Mapping.ClassMap<Address>
{
    public AddressMap()
    {
        Id(x => x.Id);
        Map(x => x.Street);
        Map(x => x.POBox);
        Map(x => x.ZIP).Length(16).Not.Nullable();
        Map(x => x.Locality).Length(128).Not.Nullable();
        References(x => x.Country).Not.Nullable();
        HasManyToMany(x => x.CustomersLivingHere)
            .Table("CustomerAddress");
    }
}

我期望做的事情基本上就是:

国家someCountry =新国家/地区                                 {                                     代码=“CL”,                                     DialPrefix =“+ 56”,                                     名称=“智利”,                                     Domain =“。ccl”                                 };

Address[] Addresses = new[] {
                                new Address
                                  {
                                      Country = someCountry,
                                      Locality = "Providencia",
                                      Street = "Pasaje Anakena 123",
                                      ZIP = "7510115"
                                  },

                                new Address
                                  {
                                      Country = someCountry,
                                      Locality = "Providencia",
                                      Street = "Perez Valenzuela 1520",
                                      ZIP = "7500035"
                                  }
};



Customer[] Customers = new[] {
                             new Customer
                             {
                                     Addresses = new[] {Addresses[0], Addresses[1]},
                                     Firstname = "Jane",
                                     Lastname = "Doe"
                             }

};

using (ISession session = _sessionFactory.OpenSession())
{
    using (ITransaction transaction = session.BeginTransaction())
    {
        foreach (Customer customer in Customers)
        {
            session.Save(customer);
        }
        transaction.Commit();
    }
}

运行包含与此类似的代码的单元测试基本上会产生 NHibernate.StaleStateException:意外的行数:0;预期:1 我阅读了大约35个博客条目和SO问题,但那些主要处理指定的ID,我没有使用 - 我看了一下Fluent Hibernate生成的映射文件,而生成器类总是“身份”。

我看了一下NHibernate的SQL输出和调试日志,似乎NHibernate以某种方式将id分配给某些对象而不是默认值导致NHibernate尝试发出UPDATE而不是SELECT语句 - 我不知道但是,为什么呢。

任何有关如何纠正这一点的见解,甚至是以良好方式建模这种关系的一般提示肯定会受到赞赏。

1 个答案:

答案 0 :(得分:2)

您已将Customer-&gt;地址映射为一对多关系(您的流利映射中的客户HasMany地址),但地址 - &gt;客户为多对多关系。 NHibernate将表示Customer-&gt; Address(HasMany)作为Address表上的CustomerId外键。这不是你想要的。

public class CustomerMap : FluentNHibernate.Mapping.ClassMap<Customer>
{
    public CustomerMap()
    {
        Id(x => x.Id);

        Map(x => x.Firstname).Length(256);
        Map(x => x.Lastname).Length(256);
        Map(x => x.Birthdate);

        HasManyToMany(x => x.Addresses)
            .Table("CustomerAddress")
            .Inverse()
            .Cascade.All();
    }
}

这种类型的双向多对多映射在NHibernate文档中进行了讨论:

http://nhibernate.info/doc/nh/en/index.html#collections-bidirectional