Когда дело доходит до базы данных, наследование TPT реализуется с Ассоциацией общего первичного ключа между базовым классом (например, Product) и всеми производными классами (например, SpecializedProduct). Теперь, когда вы удаляете объект Customer без извлечения его свойства Products, EF не подозревает, что у этого Customer есть группа продуктов, которые также необходимо удалить в соответствии с вашими требованиями. Если вы включите каскадное удаление, отметив требуемую связь между клиентом и продуктом, то database позаботится об удалении дочерних записей из таблицы продуктов, но если эта дочерняя запись является SpecializedProduct, то связанная строка на SpecializedProduct не будет удален и, следовательно, исключение, которое вы получаете. Таким образом, следующий код не будет работать:
// This works only if customer's products are not SpecializedProduct
Customer customer = context.Customers.Single(c => c.CustomerId == 1);
context.Customers.Remove(customer);
context.SaveChanges();
Этот код заставит EF отправить в базу данных следующий SQL:
exec sp_executesql N'delete [dbo].[Customer] where ([CustomerId] = @0)',N'@0 int',@0=1
При этом невозможно включить каскадное удаление между таблицами Product и SpecializedProduct, просто EF Code First реализует наследование TPT, и вы не можете его переопределить.
Так в чем же решение?
Одним из способов является то, что вы уже выяснили, вручную включив каскады между таблицами Product и SpecializedProduct, чтобы избежать исключения при удалении клиента с помощью SpecializedProducts.
Второй способ - позволить EF позаботиться о специализированных продуктах клиента при его удалении. Как я уже говорил ранее, это происходит из-за того, что объект Customer не был правильно извлечен, а EF не знает о специализированных клиентских продуктах, что означает, что при правильном извлечении объекта Customer Ef начнет отслеживать ассоциации клиентов и отправит необходимые операторы SQL, чтобы убедиться, что что каждая связанная запись удаляется перед удалением клиента:
Customer customer = context.Customers
.Include(c => c.Products)
.Single(c => c.CustomerId == 1);
context.Customers.Remove(customer);
context.SaveChanges();
В результате EF отправит в базу данных следующие операторы SQL, которые удалят все по порядку:
exec sp_executesql N'delete [dbo].[SpecializedProduct] where ([Id] = @0)',N'@0 int',@0=1
exec sp_executesql N'delete [dbo].[Product] where (([Id] = @0) and ([Customer_CustomerId] = @1))',N'@0 int,@1 int',@0=1,@1=1
exec sp_executesql N'delete [dbo].[Customer] where ([CustomerId] = @0)',N'@0 int',@0=1