I don't understand what EF is talking there about "not exposed foreign keys" in the exception. I would consider the inner exception as the important part:
Unable to determine a valid ordering
for dependent operations. Dependencies
may exist due to foreign key
constraints, model requirements, or
store-generated values.
I think the problem in your model is that you have a mutual dependency between Customer
and Address
: An address needs a customer (you have marked it as required in your mapping code) and on the other hand a customer needs an address (the default address is required both due to the non-nullable foreign key and due to your mapping code). So, EF doesn't know which entity to save first in your example code - the default address or the customer? Both entities need the primary key of the other to be saved with valid FK contraints.
The easiest solution I can see is to make the default address optional in your model and then save twice (I omit the mappings which work by convention anyway):
public class Customer
{
private ICollection<Address> m_Addresses;
public Customer() { Addresses = new List<Address>(); }
public int Id { get; set; }
public string CompanyName { get; set; }
public virtual ICollection<Address> Addresses { get { ... } set { ... } }
public Address DefaultAddress { get; set; }
public int? DefaultAddressId { get; set; } // FK for optional relationship
}
public class Address
{
public int Id { get; set; }
public string Town { get; set; }
public Customer Customer { get; set; }
}
// ...
public class CustomerConfiguration : EntityTypeConfiguration<Customer>
{
public CustomerConfiguration() : base()
{
Property(p => p.CompanyName)
.HasColumnName("Name")
.IsRequired();
HasMany(c => c.Addresses)
.WithRequired(a => a.Customer)
.Map(x => x.MapKey("CustomerId"));
}
}
public class AddressConfiguration : EntityTypeConfiguration<Address>
{
public AddressConfiguration() : base()
{
Property(p => p.Town)
.HasColumnName("Town")
.IsRequired();
}
}
And then your program would look like this:
static void Main(string[] args)
{
Customer headOffice = new Customer();
headOffice.CompanyName = "C1";
Address address = new Address();
address.Town = "Colchester";
headOffice.Addresses.Add(address);
address = new Address();
address.Town = "Norwich";
headOffice.Addresses.Add(address);
//headOffice.DefaultAddress = address;
//We don't set the default address here as SaveChanges would throw an
//exception. But because it is optional now we are allowed to leave it null.
MyContext context = new MyContext(ConnectionString);
context.Customers.Add(headOffice);
context.SaveChanges();
headOffice.DefaultAddress = address; // headoffice and address have now PKs
context.SaveChanges(); // Updates headoffice in the DB with default address
}
This double SaveChanges
is ugly, but I don't see another way.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…