Подтвердить что ты не робот

Entity Framework: установите правило удаления с помощью CodeFirst

Я использую 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
}

Моя цель - установить определенное "Delete Rule" для отношения Guest-Language. Когда язык удаляется, я не хочу удалять соответствующих гостей (так что НИКАКО каскадное удаление). Вместо этого я хочу, чтобы гостевой LanguageID был "Set NULL".

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

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

4b9b3361

Ответ 1

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

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

Unit Test:

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 с нулевым значением DB для столбца FK LanguageID.


Обновление:

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

enter image description hereenter image description here

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

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

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 при попытке вручную удалить запись. Причина этого заключается в том, что во втором Unit Test у нас нет связанного объекта-гостя, загруженного в контекст, чтобы EF не знал об этом и не представил необходимый оператор обновления, как это было в первом примере.

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

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");
}

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

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