entity framework 4 - one - Entity Framework: establecer regla de eliminación con CodeFirst
primary key composite entity framework (2)
Con Entity Framework Core, hice algo como esto ...
modelBuilder.Entity<Guest>()
.HasOne(g => g.Language)
.WithMany(l => l.Guests) // I needed a return collection
.OnDelete(DeleteBehavior.SetNull);
Estoy usando EF4 CTP 5, CodeFirst.
Por favor vea mis clases primero:
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
}
Mi objetivo es establecer una cierta "Regla de eliminación" para la relación Idioma-invitado. Cuando se elimina un idioma, no deseo eliminar los invitados correspondientes (por lo tanto, NO se eliminará en cascada). En su lugar quiero que el LanguageID del invitado sea "Establecer NULL".
Esperaba que la API fluida me apoyara aquí. Pero no pude encontrar nada útil además de .WillCascadeOnDelete (bool), que no proporciona las opciones que necesito. ¿Yo me perdí algo? ¿O esto simplemente no está implementado en CTP 5?
¡Gracias por cualquier ayuda!
Lo que está buscando se puede lograr mediante la configuración de una asociación opcional entre las entidades Guest
y de Language
:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Guest>()
.HasOptional(p => p.PreferredLanguage)
.WithMany()
.HasForeignKey(p => p.LanguageID);
}
Prueba de unidad:
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();
}
Como resultado, terminaremos teniendo un registro de guest
con un valor nulo de DB para la columna LanguageID FK.
Actualizar:
Primero veamos por qué la prueba de unidad anterior tuvo éxito al buscar en el Analizador de SQL. A continuación se muestra la traza justo después de llamar al segundo método SaveChanges ():
Como puede ver, EF es lo suficientemente inteligente como para actualizar primero el registro de invitado estableciendo su LanguageID
en nulo y luego enviar una declaración de eliminación para eliminar el registro de idioma, que es el comportamiento predeterminado de EF cuando configura una asociación opcional. Así que EF se ha ocupado del lado de la aplicación por parte de EF y, por supuesto, obtendrá un error del DBMS si intenta eliminar manualmente el registro de idioma dentro del SQL Server, como también mencionó.
Sin embargo, hay más en esta historia. Considere la siguiente prueba unitaria:
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();
}
Este falla al lanzar una SQLException contiene el mensaje exacto que recibió de SQL Server al intentar eliminar manualmente el registro. El motivo es que en la segunda prueba unitaria no tenemos el objeto invitado relacionado cargado en el contexto, por lo que EF no lo sabe y no enviará la instrucción de actualización necesaria como lo hizo en el primer ejemplo.
Volviendo a su pregunta, desafortunadamente, el Código de EF no permite cambiar explícitamente la regla de eliminar / actualizar en las relaciones, pero siempre podemos recurrir al método SqlCommand como puede ver en este artículo . En su caso, podemos codificar:
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");
}
Que es lo que buscas. Con el método de semillas anterior en su lugar, la segunda prueba de unidad también pasará.
Espero que esto ayude,
Morteza