NHibernation:级联不更新子对象
好吧,这将是一堵文字墙,但是......基本上,我有以下 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");
}
}
我期望做的基本上只是以下内容:
Country someCountry = new Country { 代码=“CL”, 拨号前缀 = "+56", 名称=“智利”, 域名=“.cl” };
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: Unexpected row count: 0;预计:1 我阅读了大约 35 篇博客文章和 SO 问题,但这些大多涉及分配的 id,我没有使用它们 - 我查看了 Fluent Hibernate 生成的映射文件,生成器类始终是“identity”。
我查看了 NHibernate 的 SQL 输出和调试日志,似乎 NHibernate 以某种方式为某些对象分配了 id,而不是默认值,这导致 NHibernate 尝试发出 UPDATE 而不是 SELECT 语句 - 但我不知道为什么。
任何有关如何纠正此问题的见解,甚至是有关以良好方式建模这种关系的一般提示,都将受到赞赏。
Ok, this is going to be a wall of text, but....basically, I have the following 3 classes:
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; }
}
This is some kind of address book (it's obviously a little more complicated in the real world, but I reduced my unit tests to this non-working case already).
Addresses are validated and normalized using a web service provided by a third party, so ideally, I'd like the relationship between customers and addresses to be many-to-many, so that each and every address in the database needs to be validated only once (we are paying money for the validation service) while still being associated with any number of customers.
At the same time, I'd like for NHibernate to automatically store addresses as soon as I assign them to a customer object and save that, but so far, this isn't working at all.
This is how my mapping files look for the Customer class:
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();
}
}
...and this is the address mapping:
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");
}
}
What I expect to do is basically just something along the lines of:
Country someCountry = new Country
{
Code = "CL",
DialPrefix = "+56",
Name = "Chile",
Domain = ".cl"
};
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();
}
}
Running a unit test containing code similar to this basically yields an NHibernate.StaleStateException: Unexpected row count: 0; expected: 1
I read about 35 blog entries and SO questions, but those mostly dealt with assigned ids, which I am not using - I took a look at the mapping files generated by Fluent Hibernate, and the generator class is always "identity".
I took a look at the SQL output and debug logs of NHibernate, and it seems NHibernate somehow assigned ids to some of the objects instead of the default which leads to NHibernate trying to emit UPDATE instead of SELECT statements - I have no idea why, though.
Any insight how to correct this, or even general tips on modelling this kind of relationship in a good way would sure be appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您已将 Customer->Address 映射为一对多关系(在您的流畅映射中,Customer HasMany Address),但将 Address->Customer 映射为多对多关系。 NHibernate 将把 Customer->Address (HasMany) 表示为 Address 表上的 CustomerId 外键。这不是你想要的。
NHibernate 文档中讨论了这种类型的双向多对多映射:
http://nhibernate.info/doc/nh/en/index.html#collections-bidirection
You have mapped the Customer->Address as a one-to-many relationship (Customer HasMany Address in your fluent mapping), but Address->Customer as a many-to-many. NHibernate is going to represent Customer->Address (HasMany) as a CustomerId foreign key on the Address table. This is not what you want.
This type of bi-directional many-to-many mapping is discussed in the NHibernate docs here:
http://nhibernate.info/doc/nh/en/index.html#collections-bidirectional