Я не понимаю, что EF говорит там об "незащищенных внешних ключах" в исключении.Я бы рассмотрел внутреннее исключение как важную часть:
Невозможно определить действительный порядок для зависимых операций.Зависимости могут существовать из-за ограничений внешнего ключа, требований модели или сгенерированных в магазине значений.
Я думаю, что проблема в вашей модели заключается в том, что у вас есть взаимозависимость между Customer
и Address
:Для адреса нужен клиент (вы пометили его как обязательный в коде сопоставления), а с другой стороны, клиенту нужен адрес (адрес по умолчанию: обязательный , так какобнуляемый внешний ключ и из-за вашего кода сопоставления).Итак, EF не знает, какой объект сохранить первым в вашем примере кода - адрес по умолчанию или клиент?Обе сущности нуждаются в первичном ключе другого, который должен быть сохранен с действительными ограничениями FK.
Самое простое решение, которое я вижу, это сделать адрес по умолчанию необязательным в вашей модели и затем сохранить дважды (я опускаю сопоставления, которые работаютв любом случае по соглашению):
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();
}
}
И тогда ваша программа будет выглядеть так:
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
}
Этот двойной SaveChanges
ужасен, но я не вижу другого пути.