2011-07-19 7 views
10

Estoy utilizando EF4.1 con el código primero y la herencia TPT (tabla por tipo). Tengo una estructura como estaPrimero el código TPT y en cascada en eliminar

public class Customer 
{ 
    public virtual ICollection<Product> Products {get; set;} 
} 

public class Product 
{ 
    [Required] 
    public int Id { get; set; } 

    [Required] 
    public virtual Customer {get; set;} 

    public decimal Price { get; set; } 
} 

public class SpecializedProduct : Product 
{ 
    public string SpecialAttribute { get; set; } 
} 

cuando se borra un cliente Quiero que todos los productos asociados a ese cliente que desea eliminar. Puedo especificar un WillCascadeOnDelete (verdadero) entre el cliente y el producto:

modelBuilder.Entity<Customer>().HasMany(e => e.Products).WithRequired(p => p.Customer).WillCascadeOnDelete(true); 

pero ya que no hay una relación de clave foreighn entre SpecializedProduct y Producto obtengo una excepción al intentar eliminar el cliente:

La instrucción DELETE entró en conflicto con la restricción REFERENCE "SpecializedProduct _TypeConstraint_From_Product_To_SpecializedProduct". El conflicto ocurrió en la base de datos "Test", tabla "dbo.SpecializedProduct", columna "Id". La instrucción se ha terminado.

si fijo manualmente una de supresión en cascada en el SpecializedProduct _TypeConstraint_From_Product_To_SpecializedProduct constreñimiento funciona, pero me gustaría ser capaz de especificar esto usando el modelbuilder o de alguna otra manera en el código. es posible?

¡Gracias de antemano!

Best Regards

Simon

Respuesta

9

Cuando se trata de la base de datos, un TPT inheritance se implementa con un Shared Primary Key Association entre la clase base (por ejemplo, del producto) y todas las clases derivadas (por ejemplo SpecializedProduct). Ahora, cuando elimina un objeto de Cliente sin recuperar su propiedad de Productos, EF no tiene idea de que este Cliente tiene un grupo de productos que también debe eliminarse según sus necesidades. Si habilita las eliminaciones en cascada marcando su asociación cliente-producto según sea necesario, la base de datos se encargará de eliminar los registros secundarios de la tabla de productos, pero si este registro secundario es un producto especializado, la fila relacionada en el producto especializado ganó. no se borra y, por lo tanto, la excepción que está recibiendo. Así que básicamente el siguiente código no funcionará:

// This works only if customer's products are not SpecializedProduct 
Customer customer = context.Customers.Single(c => c.CustomerId == 1); 
context.Customers.Remove(customer); 
context.SaveChanges();  

Este código hará que EF para enviar el siguiente código SQL a la base de datos:

exec sp_executesql N'delete [dbo].[Customer] where ([CustomerId] = @0)',N'@0 int',@0=1 


Dicho esto, no hay manera para permitir que la cascada elimine las tablas Producto y Producto especializado, así es como EF Code First implementa una herencia TPT y no puede anularla.

¿Cuál es la solución?

Una forma es la que ya ha descubierto, cambiando manualmente las cascadas entre las tablas Product y SpecializedProduct para evitar la excepción al eliminar un cliente con SpecializedProducts.

La segunda forma es dejar que EF se ocupe de los productos especializados del cliente cuando retira al cliente.Como dije antes, esto sucede porque el objeto Cliente no se ha recuperado correctamente y EF no tiene conocimiento de los Productos Especializados del cliente, lo que significa que al buscar el objeto del cliente correctamente, Ef comenzará a rastrear las asociaciones del cliente y enviará las declaraciones SQL necesarias para asegurarse que cada registro relacionado se retira antes de retirar el cliente:

Customer customer = context.Customers 
          .Include(c => c.Products) 
          .Single(c => c.CustomerId == 1); 

context.Customers.Remove(customer); 
context.SaveChanges();  

Como resultado, EF presentará las siguientes sentencias SQL a la base de datos que elimina perfectamente todo en orden:

exec sp_executesql N'delete [dbo].[SpecializedProduct] where ([Id] = @0)',N'@0 int',@0=1 

exec sp_executesql N'delete [dbo].[Product] where (([Id] = @0) and ([Customer_CustomerId] = @1))',N'@0 int,@1 int',@0=1,@1=1 

exec sp_executesql N'delete [dbo].[Customer] where ([CustomerId] = @0)',N'@0 int',@0=1 
+3

se tomó 13 meses, pero Obtuve mi respuesta :-) ¡Gracias! Como recuerdo, terminé ejecutando algunos sql para definir manualmente la eliminación de cascada en la creación del modelo. –

Cuestiones relacionadas