2011-12-16 11 views
6

Estoy intentando que mi lógica de negocios no conozca el funcionamiento interno de mi Data Layer y vica versa.¿Hay alguna manera de encontrar todas las entidades que han eliminado sus relaciones?

Pero Entity Framework lo está haciendo difícil. Puedo insertar en una colección (en mi capa empresarial) sin una referencia a la ObjectContext:

order.Containers.Add(new Container { ContainerId = containerId, Order = order }); 

Y que salva bien cuando llega el momento de hacer un SaveChanges() en la capa de datos.

Pero para eliminar un elemento de una colección, necesito una referencia al ObjectContext. (Soy el caso # 1 en este guide to deleting EF Entities.) Si acabo de hacer esto:

delContainers.ForEach(container => order.Containers.Remove(container)); 

Entonces cuando llamo SaveChanges() me sale una excepción que me dice que tengo que eliminar el objeto, así como la referencia.

Por lo tanto, mis opciones como lo veo son:

  1. Para pasar un delegado a mi lógica de negocios que va a llamar a la Entidad marco ObjectContext Eliminar método.
  2. O (estoy esperando) encontrar una forma de obtener todas las entidades que han tenido su referencia eliminada y realmente eliminarlas. (Justo antes de llamar SaveChanges() en mi capa de datos.)

¿Alguien sabe de una manera de hacer eso?

ACTUALIZACIÓN:

yo probamos este:

// Add an event when Save Changes is called 
this.ObjectContext.SavingChanges += OnSavingChanges; 

...

void OnSavingChanges(object sender, EventArgs e) 
{ 
    var objectStateEntries = ObjectContext.ObjectStateManager 
            .GetObjectStateEntries(EntityState.Deleted); 

    foreach (var objectStateEntry in objectStateEntries) 
    { 
     if (objectStateEntry.IsRelationship) 
     { 
      // Find some way to delete the related entity 
     } 
    } 
} 

Pero ninguno a pesar de que he eliminado una relación, el conjunto de elementos eliminados está vacía.

(I trató de ver todos los artículos demasiado y mi relación no está ahí. Es evidente que hay algo fundamental que no consigo sobre ObjectStateManager.)

+0

Relacionados: http://stackoverflow.com/questions/20707344/entity-framework-6-detect-relationship-changes – Nathan

Respuesta

4

La solución correcta para EF es el punto 3. del artículo vinculado. Significa propagar FK a entidad principal en PK para entidad dependiente. Esto formará algo llamado identifying relation que borra automáticamente la entidad dependiente cuando se elimina de la entidad padre.

Si no desea cambiar su modelo y aún desea lograrlo de forma persistente, ignorante, probablemente pueda hacerlo, pero solo funcionará para independent associations. Algunos implementación inicial que funciona, al menos para mi sencilla solución probada:

public partial class YourObjectContext 
{ 
    public override int SaveChanges(SaveOptions options) 
    { 
     foreach (ObjectStateEntry relationEntry in ObjectStateManager 
              .GetObjectStateEntries(EntityState.Deleted) 
              .Where(e => e.IsRelationship)) 
     { 
      var entry = GetEntityEntryFromRelation(relationEntry, 0); 
      // Find representation of the relation 
      IRelatedEnd relatedEnd = entry.RelationshipManager 
              .GetAllRelatedEnds() 
              .First(r => r.RelationshipSet == relationEntry.EntitySet); 

      RelationshipType relationshipType = relatedEnd.RelationshipSet.ElementType; 
      if (!SkipDeletion(relationshipType)) 
      { 
       // Now we know that model is inconsistent and entity on many side must be deleted 
       if (!(relatedEnd is EntityReference)) // related end is many side 
       { 
        entry = GetEntityEntryFromRelation(relationEntry, 1); 
       } 

       if (entry.State != EntityState.Deleted) 
       { 
        context.DeleteObject(entry.Entity); 
       } 
      } 
     } 

     return base.SaveChanges(); 
    } 

    private ObjectStateEntry GetEntityEntryFromRelation(ObjectStateEntry relationEntry, int index) 
    { 
     var firstKey = (EntityKey) relationEntry.OriginalValues[index]; 
     ObjectStateEntry entry = ObjectStateManager.GetObjectStateEntry(firstKey); 
     return entry; 
    } 

    private bool SkipDeletion(RelationshipType relationshipType) 
    { 
     return 
      // Many-to-many 
      relationshipType.RelationshipEndMembers.All(
       r => r.RelationshipMultiplicity == RelationshipMultiplicity.Many) || 
      // ZeroOrOne-to-many 
      relationshipType.RelationshipEndMembers.Any(
       r => r.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne); 
    } 
} 

para que funcione sus entidades debe ser habilitado para el cambio dinámico de seguimiento (todas las propiedades deben ser virtuales y de la entidad deben proxy) o debe llamar manualmente DetectChanges.

En el caso de asociaciones de claves foráneas, la situación será probablemente mucho peor porque no encontrará ninguna relación eliminada en el administrador de estado. Tendrá que hacer un seguimiento de los cambios en las colecciones o claves de forma manual y compararlas para encontrar discrepancias (no estoy seguro de cómo hacerlo de manera genérica) Asociación de claves extranjeras en mi humilde opinión, la relación de identificación. El uso de propiedades FK ya significa que incluyó una dependencia de persistencia adicional en su modelo.

+0

Gracias por su respuesta. Parece que no tengo suerte porque solo uso lo que está predeterminado cuando generas desde una base de datos. Eso parece ser asociaciones FK. Gracias por tu publicación de blog que aclara los dos tipos de asociaciones en EF. (¡No sabía que había más de uno!) Voy a cavar un poco más y ver si puedo encontrar alguna manera de detectar que la relación FK ha sido eliminada. (Preferiría no cambiar mi proyecto para tratar de usar Asociaciones Independientes, especialmente dado que solo se admiten para relaciones de 1 a muchas). – Vaccano

+0

Como una pregunta complementaria, ¿cómo puede tener una relación "Uno para Muchos" y tener una Relación de Identificación? ? La única forma que conozco de modelar "Uno a muchos" es tener el PK de la tabla "Uno" como columna en la tabla "Muchos" y usarlo como FK (volver a la tabla "Uno"). ¿Hay alguna manera de modelar una relación de identificación para que tenga una relación de uno a muchos que permita eliminar una referencia y eliminar automáticamente la entrada asociada en la tabla "Muchos"? – Vaccano

+1

¿Ha revisado mi respuesta vinculada donde describo la relación de identificación. Por supuesto que puedes usarlo de uno a muchos. Solo necesita hacer una PK compuesta en una entidad dependiente que consista en su propia clave y FK. –

2

Una forma es escribir un manejador de cambio en sus datos capa:

private void ContainersChanged(object sender, 
     CollectionChangeEventArgs e) 
    { 
     // Check for a related reference being removed. 
     if (e.Action == CollectionChangeAction.Remove) 
     { 
      Context.DeleteObject(e.Element); 
     } 
    } 

Hay muchos lugares que usted puede cablear esto - en el constructor de su objeto o repositorio de obtener o SavingChanges o donde sea:

entity.Containers.AssociationChanged += new CollectionChangeEventHandler(ContainersChanged); 

Ahora puede eliminar la asociación de otro lugar y "en cascada" a la entidad.

+0

Parece que funcionaría, pero tendría que conectarlo a cada colección. Luego, cuando alguien más agregue una colección, ellos tendrían que recordar cablearla también. (Eventualmente alguien olvidará y lanzará un error.) – Vaccano

Cuestiones relacionadas