Я нахожусь в процессе создания иерархии данных со стратегией TPH (Table Per Hierarchy), и я столкнулся с некоторыми серьезными проблемами, связанными с объединением TPH с самостоятельной ссылкой на таблицу.
Только для пояснения: сначала я использую код EF6.
Итак, без лишних слов - вся структура состоит из 3 классов:
- Транзакция - абстрактный родитель,
- Задолженность: транзакция
- Оплата: транзакция
Идея простейшего состоит в том, что Задолженность с определенным пулом (например, 100 $) может содержать несколько платежей, которые впоследствии будут суммироваться и полностью покрывать их (например, 20 $, 50 $, 30 $).
public abstract class Transaction
{
[Key]
public int Id { get; set; }
public decimal MoneyPool { get; set; }
public DateTime Date { get; set; }
}
public class Debt : Transaction
{
public int DebtorId { get; set; }
public Debtor Debtor { get; set; }
public string Description { get; set; }
public ICollection<Transaction> Payments { get; set; }
}
public class Payment : Transaction
{
public int? DebtId { get; set; }
public Debt Debt { get; set; }
}
Теоретически TPH - это режим реализации наследования по умолчанию в EF, но я вручную добавил отображение в моем OnModelCreating просто ради своего ума и для возможности немного изменить ситуацию при необходимости.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Transaction>()
.Map<Debt>(d => d.Requires("Discriminator").HasValue("Debt"));
modelBuilder.Entity<Transaction>()
.Map<Payment>(d => d.Requires("Discriminator").HasValue("Payment"));
base.OnModelCreating(modelBuilder);
}
Теперь, хорошая новость в том, что получение / публикация долгов и привязка платежей к ним работает просто отлично .
проблема
- это то, что я хотел бы иметь возможность удалить долг и вручную указать, что делать с его дочерними элементами (платежи) - будь то каскадное их удаление или аннулирование свойства FK.
Я хотел проверить, какое действие EF установил для меня при инициализации модели, поэтому запустил sp_help Transactions
в SQL Server Management Studio и заметил, что по умолчанию для delete_action и update_action задано значение «NO ACTION» в случае, если такой самостоятельной таблицы-ссылки.
Зная, что я пытался вручную изменить его:
- Внутри миграции, которая создала исходную таблицу иерархии, установив параметр cascadeDelete,
- Удаляя исходный внешний ключ с помощью вспомогательных методов EF и добавляя мой собственный, с включенным каскадным удалением,
- Выполнение вышеизложенного с необработанным SQL.
Все это привело к одной и той же ошибке:
Введение ограничения FOREIGN KEY 'FK_dbo.Transactions_dbo.Transactions_Debt_Id' в таблице 'Транзакции' может привести к возникновению циклов или нескольких каскадных путей. Укажите ON DELETE NO ACTION или ON UPDATE NO ACTION или измените другие ограничения FOREIGN KEY.
Поначалу это было довольно неожиданно, потому что это ТОЧНО , что я пытаюсь сделать - включить несколько каскадных путей. Однако по какой-то причине я не могу.
Последнее, что я попытался, - это настроить отношение между Долгом и Платежом внутри моей переопределенной OnModelCreating .
modelBuilder.Entity<Transaction>()
.Map<Payment>(d => d.Requires("Discriminator").HasValue("Payment"))
.HasOptional(t => (t as Payment).Debt)
.WithMany(t => (t as Debt).Payments)
.WillCascadeOnDelete(true);
При этом я получаю сообщение об ошибке, указывающее, что (t как T). Свойство не является допустимым способом указания свойства. Однако без приведения, как вы, наверное, уже поняли, я вообще не могу получить доступ к свойствам конкретных классов.
Я полностью застрял на этом. У кого-нибудь есть способ решить это? Я хотел бы, по крайней мере, иметь возможность установить delete_action на cascade или установить null , update_action меньше приоритета, потому что это происходит только тогда, когда кто-то изменяет Первичный ключ, который очень вряд ли произойдет в любом случае.