2014-12-31 3 views
1

Я создал веб-приложение asp.NET MVC с двумя моделями, использующими EF Code First, которые имеют отношение 1 к 0..1.Выполнение ссылочной целостности при удалении объекта в отношениях 1-к 0..1

public class ClassA 
{ 
    public int Id {get;set;} 
    //other properties 
    public virtual ClassB ClassB {get;set;} 
} 

public class ClassB 
{ 
    public int Id {get;set;} 
    //other properties 
} 

В моей базе данных это успешно создает две таблицы с ClassA с Nullable FK для ClassB. Это отлично работает, за исключением сценария, в котором удалена запись ClassA. В этом случае любая связанная запись ClassB остается в базе данных. Я знаю, что я могу вручную удалить их в методе POST Delete:

[HttpPost, ActionName("Delete")] 
[ValidateAntiForgeryToken] 
public ActionResult DeleteConfirmed(int id) 
{ 
    ClassA classA = context.ClassA.Include(c => c.ClassB).First(c => c.Id == id); 
    context.ClassB.Remove(classA.ClassB); 
    context.ClassA.Remove(classA); 
    context.SaveChanges(); 
} 

мне не очень нравится этот подход, поскольку он опирается на меня не делает ошибку, и предполагается, что этот метод является единственным способом, что запись (в какой-то момент также могут быть указаны прямые инструкции DELETE SQL в отношении базы данных). Пример, который я создал здесь, прост, но мое фактическое приложение включает в себя множество моделей и ассоциаций, и оно стало довольно сложным. Хотя мне нравится думать, что я безошибочен, я по опыту узнал, что я не являюсь, и не буду полагаться на себя, чтобы гарантировать, что записи не становятся сиротами =)

Как заставить базу данных принудительно использовать ссылочный целостность, так что, когда ClassA удаляется, все ClassB, имеющие внешние ключи в этом ClassA, также удаляются?

РЕШИТЬ (своего рода)

Как было предложено Герт, я использовал Fluent API, чтобы гарантировать, что право объект был установлен как принцип и установить все отношения каскадом на удаление. Однако я столкнулся с несколькими проблемами, в основном из-за того, что в моей базе данных уже есть данные. К счастью, я нахожусь в разработке, где я могу просто удалить все данные; в противном случае, я не уверен, как бы я это исправил.

Сначала я попытался просто добавить Fluent API и базу данных обновлений. Я получил ошибку, которая частично читается: «Либо параметр @objname неоднозначен, либо заявленный код @objtype (COLUMN) ошибочен», который, по-видимому, вызван тем, что EF пытается изменить имя существующего столбца FK. В этом случае я решил использовать 2 миграции: один для удаления существующих отношений, а другой - для добавления вновь переконфигурированных отношений. Я должен был сделать это в довольно определенной серии событий.

  1. Прокомментировал все ссылки на затронутые отношения в моем контроллере, чтобы избежать ошибок при обновлении.
  2. Прокомментировал связь в Модели.

    public class ClassA 
    { 
        public int Id {get;set;} 
        //other properties 
        //public virtual ClassB ClassB {get;set;} 
    } 
    
  3. Add-Migration and Update-Database, чтобы удалить существующие отношения.
  4. расстегнул все изменения в модель и контроллер раскомментировав все, что я комментировал в шаге 1 и 2.
  5. Настроенного новые отношения в OnModelCreating как это было предложено Гертом, чтобы сохранить новые отношения.

    modelBuilder.Entity<ClassA>() 
        .HasOptional(b => b.ClassB) 
        .WithRequired() 
        .Map(m => m.MapKey("ClassB_Id")) 
        .WillCascadeOnDelete(); 
    
  6. Add-Migration and Update-Database для создания новых отношений. Этот шаг не удался, когда в моей базе данных были данные. Если бы у меня не было возможности просто очистить базу данных всех данных, я не уверен, как бы я это сделал.

ответ

4

В один-к-одному ассоциаций вы всегда должны думать о том, какой объект является принципом и который является зависимым. Зависимый, как предполагает слово, не может существовать без другого.

В вашем дизайне принцип и зависимость неправильны: ClassA является зависимым, ClassB может жить самостоятельно. Вот как EF интерпретирует модель класса. Если вы хотите, чтобы это было сделано иначе, вам нужно будет добавить некоторые инструкции по отображению. Сохраняя модель класса без изменения, это может быть сделано только беглого API, например, в OnModelCreating переопределения данного контекста:

modelBuilder.Entity<ClassA>().HasOptional(a => a.ClassB) 
    .WithRequired().Map(m => m.MapKey("ClassAId")) 
    .WillCascadeOnDelete(); 

Теперь будет внешним ключом, ClassAId в ClassB таблице. Каскадное удаление гарантирует, что при удалении ClassA его зависимый ClassB удаляется автоматически.

+0

Спасибо за помощь! Это работало до тех пор, пока у меня не было данных уже в затронутых таблицах в базе данных. К счастью, я нахожусь в разработке, где я могу просто очистить базу данных для запуска этого обновления, но как я мог бы это сделать, если бы мне пришлось поддерживать существующие данные? – IamzombieGrrArgh

+0

Трудно сказать отсюда. Честно говоря, у меня нет практического опыта миграции, я всегда, кажется, в конечном итоге работаю с базой данных. В любом случае, похоже, вы узнали, как это сделать. –

0

Изменение внешнего ключа в базе данных SQL сервера, чтобы "ON DELETE CASCADE"

https://stackoverflow.com/a/6260736/1056639

ALTER TABLE dbo.T2 
    DROP CONSTRAINT FK_T1_T2 -- or whatever it's called 

ALTER TABLE dbo.T2 
    ADD CONSTRAINT FK_T1_T2_Cascade 
    FOREIGN KEY (EmployeeID) REFERENCES dbo.T1(EmployeeID) ON DELETE CASCADE 

 Смежные вопросы

  • Нет связанных вопросов^_^