NHibernation: cascade not updating child objects
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",
开发者_JS百科 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.
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.
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();
}
}
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
精彩评论