Entity Framework: Установить правило удаления с CodeFirst - PullRequest
19 голосов
/ 19 февраля 2011

Я использую EF4 CTP 5, CodeFirst.

Пожалуйста, сначала посмотрите мои уроки:

public class Guest
{
        [Key]
        public Guid GuestID { get; set; }

        public Language PreferredLanguage { get; set; }
        public Guid? LanguageID { get; set; }
}

public class Language
{
        [Key]
        public Guid LanguageID { get; set; }

        [Required(ErrorMessage = "Enter language name")]
        [StringLength(50, ErrorMessage = "Language name is too long")]
        public string LanguageName { get; set; } // in origine language
}

Моя цель - установить определенное «правило удаления» для отношения «Гость-язык». Когда язык удаляется, я не хочу удалять соответствующих гостей (поэтому NO каскадное удаление). Вместо этого я хочу, чтобы языковой ID гостя был «Установить NULL».

Я надеялся, что свободный API поддержит меня здесь. Но я не смог найти ничего полезного, кроме .WillCascadeOnDelete (bool), который не предоставляет нужные мне опции. Я что-то пропустил? Или это просто не реализовано в CTP 5?

Спасибо за любую помощь!

Ответы [ 2 ]

35 голосов
/ 19 февраля 2011

То, что вы ищете, может быть достигнуто путем установки необязательной ассоциации между Guest и Language сущностями:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Guest>()
                .HasOptional(p => p.PreferredLanguage)
                .WithMany()
                .HasForeignKey(p => p.LanguageID);
}

Модульный тест:

using (var context = new Context())
{
    var language = new Language()
    {
        LanguageName = "en"
    };
    var guest = new Guest()
    {
        PreferredLanguage = language
    };
    context.Guests.Add(guest);
    context.SaveChanges();

    context.Languages.Remove(language);
    context.SaveChanges();
}

В результате мы получим запись guest с нулевым значением БД для столбца LanguageID FK.


Обновление:

Сначала давайте посмотрим, почему вышеупомянутый модульный тест прошел успешно, заглянув в SQL Profiler. Ниже показана трассировка сразу после вызова второго метода SaveChanges ():

enter image description here enter image description here

Итак, как вы можете видеть, EF достаточно умен, чтобы сначала обновить гостевую запись, установив для ее LanguageID значение NULL, а затем отправить инструкцию удаления для удаления языковой записи, которая является поведением EF по умолчанию, когда вы устанавливаете необязательный параметр. ассоциация. Таким образом, EF позаботилась об этом на стороне приложения, и, конечно, вы получите сообщение об ошибке от СУБД, если попытаетесь вручную удалить языковую запись внутри SQL Server, как вы также упоминали.

Тем не менее, это еще не все в этой истории. Рассмотрим следующий модульный тест:

using (var context = new Context())
{
    var language = new Language() { LanguageName = "en" };
    var guest = new Guest() { PreferredLanguage = language };
    context.Guests.Add(guest);
    context.SaveChanges();
}

using (var context = new Context())
{
    var language = context.Languages.First();        
    context.Languages.Remove(language);
    context.SaveChanges();
}     

Этот сбой при выдаче исключения SQLException содержит точное сообщение, полученное от SQL Server при попытке удалить запись вручную. Причина этого в том, что во втором модульном тесте у нас нет соответствующего гостевого объекта, загруженного в контексте, поэтому EF не знает об этом и не будет отправлять необходимый оператор обновления, как это было в первом примере.

Возвращаясь к вашему вопросу, к сожалению, EF Code First не позволяет явно изменять правило удаления / обновления для отношений, но мы всегда можем прибегнуть к методу SqlCommand , как вы можете увидеть его пример в * 1036. * этот пост . В вашем случае мы можем кодировать:

protected override void Seed(Context context)
{
    context.Database.SqlCommand("ALTER TABLE dbo.Guests DROP CONSTRAINT Guest_PreferredLanguage");
    context.Database.SqlCommand("ALTER TABLE dbo.Guests ADD CONSTRAINT Guest_PreferredLanguage FOREIGN KEY (LanguageID) REFERENCES dbo.Languages(LanguageID) ON UPDATE NO ACTION ON DELETE SET NULL");
}

Что вы ищете. При наличии вышеуказанного метода посева второй модульный тест также пройдет.

Надеюсь, это поможет,
Мортеза

0 голосов
/ 02 февраля 2018

С Entity Framework Core я сделал что-то вроде этого ...

modelBuilder.Entity<Guest>()
   .HasOne(g => g.Language)
   .WithMany(l => l.Guests) // I needed a return collection
   .OnDelete(DeleteBehavior.SetNull);
...