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
}
моя цель-установить определенное "правило удаления" для отношений гостевой язык. Когда язык удаляется, I не хочется удалить соответствующих гостей (так нет каскадное удаление). Вместо этого я хочу, чтобы LanguageID гостя был "Set NULL".
Я надеялся, что fluent API поддержит меня здесь. Но я не смог найти ничего полезного. кроме того.WillCascadeOnDelete (bool), который не предоставляет необходимых мне опций. Я что-нибудь пропустил? Или это просто не реализовано в CTP 5?
Спасибо за любую помощь!
2 ответов
то, что вы ищете может быть достигнуто путем создания дополнительные ассоциации между 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
запись со значением DB null для столбца LANGUAGEID FK.
обновление:
сначала давайте посмотрим, почему вышеприведенный модульный тест удалось заглянуть в SQL Profiler. Ниже показана трассировка сразу после вызова второго метода SaveChanges() :
Итак, как вы можете видеть, EF достаточно умен, чтобы сначала обновить гостевую запись, установив ее LanguageID
в значение null, а затем отправьте инструкцию delete для удаления записи языка, которая является поведением 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 сначала не позволяет явно изменять правило удаления / обновления отношений, но мы всегда можем прибегнуть к 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");
}
это то, что вы ищете. С иметь вышеуказанный метод семени на месте, второе испытание блока тоже пройдет.
надеюсь, это поможет,
Мортеза!--11-->
С ядром Entity Framework я сделал что-то вроде этого...
modelBuilder.Entity<Guest>()
.HasOne(g => g.Language)
.WithMany(l => l.Guests) // I needed a return collection
.OnDelete(DeleteBehavior.SetNull);