21

Estamos utilizando primero el código del Entity Framework con las relaciones de clave externa. Investigamos formas de manejar la eliminación de objetos de una ICollection de entidades en nuestra aplicación.¿Es posible eliminar un elemento secundario de la colección y resolver problemas en SaveChanges?

Cuando tenemos una entidad con relaciones secundarias, podemos agregar objetos directamente a su colección usando el método Agregar. Ahora cuando se utiliza retire se obtiene el error

System.InvalidOperationException se produjo Mensaje = La operación fracasó : La relación no podía ser cambiado debido a que uno o más de las propiedades de clave externa es no anulable. Cuando se realiza un cambio en una relación , la propiedad de clave foránea relacionada se establece en un valor nulo. Si la clave externa no admite valores nulos, se debe definir una nueva relación , se debe asignar a la propiedad clave externa valor no nulo o se debe eliminar el objeto no relacionado.

Entiendo que esto se debe a que Eliminar en la colección solo elimina la relación anulando la clave externa. Queríamos escribir nuestra lógica comercial en nuestra entidad y permitir su eliminación.

Así que obtenga la entidad raíz fuera de su Repositorio por ejemplo Pedido de OrderRepository luego llame a algún método específico de la entidad, p. Order.AddOrderline(Orderline orderline) Esto añade un OrderLine a las órdenes virtual ICollection<OrderLine> OrderLines

Sin embargo, no podemos escribir código como Order.CancelOrderline(int orderLineId) debido a la simple eliminación de la ICollection produce un error en los cambios de ahorro.

No parece ser de todos modos para lograr esto simplemente manipulando las colecciones de objetos. Obviamente, podemos eliminar directamente del contexto. Sin embargo, me gustaría hacer que forme parte de la entidad. ¿Podemos limpiar ciertas entidades sin clave externa en el evento SaveChanges de Entity Framework? Obviamente, es necesario decirle a EF qué entidades se pueden eliminar si tienen una clave externa nula.

Actualmente estamos utilizando un patrón de repositorio para que el controlador no tenga acceso al contexto. Obviamente, podría utilizar un repositorio OrderLine o un método Remove OrderLine en el repositorio de pedidos. Sin embargo, me preguntaba si era posible escribir el código en la entidad sin referencias al mecanismo de persistencia.

¿Pensamientos? ¿Nos estamos equivocando? ¿Otros ORM le permiten eliminar de Colecciones para niños?

Respuesta

38

No sé si la siguiente es una solución para usted, pero Entity Framework es compatible con Identifying Relationships. En dicha relación, la clave externa de la entidad hijo (dependiente) del padre (principal) debe ser parte de la clave primaria (compuesta) de la entidad hijo.Por ejemplo - con anotaciones de datos DbContext - clases del modelo tienen que tener este aspecto:

public class Order 
{ 
    [Key] 
    public int OrderId { get; set; } 

    public ICollection<OrderLine> OrderLines { get; set; } 
} 

public class OrderLine 
{ 
    [Key, ForeignKey("Order"), Column(Order = 1)] 
    public int OrderId { get; set; } 

    [Key, Column(Order = 2)] 
    public int OrderLineId { get; set; } 

    public Order Order { get; set; } 
} 

Puede hacer que el OrderLineId una identidad generada automáticamente si lo desea. Importante es solo que el FK a Order es parte del PK.

un código como este por ejemplo ...

using (var ctx = new MyContext()) 
{ 
    var order = ctx.Orders.Include("OrderLines").Single(o => o.OrderId == 1); 
    var orderLineToDelete = order.OrderLines 
     .FirstOrDefault(ol => ol.OrderLineId == 5); 
    if (orderLineToDelete != null) 
     order.OrderLines.Remove(orderLineToDelete); 

    ctx.SaveChanges(); 
} 

... habría hecho eliminar el orderLineToDelete de la base de datos.

Más detalles son here en la sección "Consideraciones sobre la identificación y las relaciones no identificables".

+0

Ah, si si hago una clave compuesta en las entidades secundarias, EF se encargará de esto por mí. Ahora eso parece bastante atractivo. – GraemeMiller

+6

Eso fue genial, pero necesitaba agregar [Clave, columna (Orden = 0), DatabaseGenerated (DatabaseGeneratedOption.Identity)] public int OrderLineId {get; conjunto; } para que no se queje de la inserción de identidad. – GraemeMiller

+0

@GraemeMiller: Eso es lo que quise decir con "* Puedes hacer que OrderLineId sea una identidad autogenerada si quieres *" :) Olvidé mencionar que necesitas hacer esto en el * modelo * (con DatabaseGeneratedOption) * y * en la base de datos*. – Slauma

2

Como puede ver, si elimina una Entidad de una colección, la Entidad cuelga unida al contexto del objeto y le da un error cuando llama al SaveChanges(); He utilizado eventos de dominio para habilitar una forma ordenada de eliminar la entidad del contexto del objeto a través de un repositorio.

He detallado este enfoque en mi respuesta to this question.

+0

Parece un enfoque realmente interesante. ¿Es similar al patrón de comando? Me gusta el concepto de modelar eventos de dominio. Realmente necesito terminar de leer mi libro azul. – GraemeMiller

+1

Hmm ... realmente no Comando, no creo, los eventos de dominio son objetos sin comportamiento que indican que algo le ha sucedido a cualquier objeto que los maneje. Creo que primero leí sobre ellos de [Martin Fowler] (http://martinfowler.com/eaaDev/DomainEvent.html), pero [este artículo] (http://www.udidahan.com/2009/06/14/ domain-events-salvation) fue el que realmente me ayudó a comprender cuán poderosos son :) –

+0

Genial, tiene sentido ya que no tienen carga útil como dices. Se leerá sobre eso. Apreciar las referencias adicionales. – GraemeMiller

Cuestiones relacionadas