2009-12-21 22 views
9

Estoy trabajando con una base de datos donde los diseñadores decidieron marcar cada tabla con una columna de bits IsHistorical. No se tiene en cuenta el modelado adecuado y no hay forma de que pueda cambiar el esquema.Eliminaciones suaves (columna IsHistorical) con EntityFramework

Esto está causando cierta fricción cuando se desarrollan pantallas CRUD que interactúan con las propiedades de navegación. No puedo simplemente tomar un Producto y luego editar su EntityCollection. Tengo que escribir manualmente verificaciones IsHistorical por todos lados y me está volviendo loco.

Las adiciones también son horribles porque hasta ahora he escrito todas las comprobaciones manuales para ver si una adición es simplemente borrada, así que en lugar de agregar una entidad duplicada, puedo alternar IsHistoric.

Las tres opciones que he consideradas son:

  1. Modificación de los modelos T4 a incluir controles IsHistorical y sincronización.

  2. Interceptar eliminaciones y adiciones en ObjectContext, alternar la columna IsHistorical y sincronizar el estado del objeto.

  3. Suscríbase al evento AssociationChanged y active la columna IsHistorical allí.

¿Alguien tiene alguna experiencia con esto o podría recomendar el enfoque más sencillo?

Nota: Sí, lo sé, este es un mal modelado. He leído los mismos artículos sobre eliminaciones suaves que tienes. Apesta que tengo que lidiar con este requisito, pero lo hago. Solo quiero el método más sencillo para tratar con eliminaciones suaves sin escribir el mismo código para cada propiedad de navegación en mi base de datos.

Nota # 2 La respuesta de LukeLed es correcta desde el punto de vista técnico, aunque te obliga a tener un patrón ORM, sin gráfico, pobremente malo. El problema radica en el hecho de que ahora estoy obligado a arrancar todos los objetos "eliminados" del gráfico y luego llamar al método Eliminar sobre cada uno. Eso no me va a ahorrar tanta codificación ceremonial manual. En lugar de escribir verificaciones IsHistoric manuales ahora estoy reuniendo objetos eliminados y recorriendo los mismos.

+3

Siento tu dolor y supervisaré este hilo con cuidado. Espero que tengas una respuesta! –

+1

¿Por qué dices que es realmente malo/malo? ¿Podrías explicar más? – LukLed

Respuesta

5

Como estoy seguro de que está consciente, no va a ser una gran solución a este problema cuando no se puede modificar el esquema.Teniendo en cuenta que no le gusta la opción de repositorio (sin embargo, se pregunta si no estás siendo un poco precipitada para descartarlo), esto es lo mejor que puedo llegar a:

  1. manija ObjectContext.SavingChanges
  2. Cuando se desencadene ese evento, rastree el ObjectStateManager buscando objetos en el estado eliminado. Si tienen una propiedad IsHistorical, configúrelo y cambie el estado del objeto a modificado.

Esto podría ser complicado cuando se trata de asociaciones/relaciones, pero creo que hace más o menos lo que quiere.

8

Estoy usando repositorio genérico en mi código. Se podía hacerlo como:

public class Repository<T> : IRepository<T> where T : EntityObject 
{ 
    public void Delete(T obj) 
    { 
     if (obj is ISoftDelete) 
      ((ISoftDelete)obj).IsHistorical = true 
     else 
      _ctx.DeleteObject(obj); 
    } 

Su método List() filtraría por IsHistorical también.

EDIT:

ISoftDelete interfaz:

public interface ISoftDelete 
{ 
    bool IsHistorical { get; set; } 
} 

clases de entidad pueden ser fácilmente marcados como ISoftDelete, porque son parciales. definición parcial de clase necesita ser añadido en archivo separado:

public partial class MyClass : EntityObject, ISoftDelete 
{ 

} 
+0

Si 'T' es un' EntityObject', ¿cómo puede heredarse también de la interfaz 'ISoftDelete'? ¿No es esa herencia múltiple (no permitida en .Net)? –

+0

@Yaakow Ellis: Eso está permitido. Puede heredar de una clase, pero puede implementar tantas interfaces como desee. 'ISoftDelete' es la interfaz. – LukLed

+0

por lo que, en otras palabras, 'ISoftDelete' define una propiedad:' IsDeleted'. No hay una sola línea de código donde diga que 'clase X' (' T' en este ejemplo) hereda de 'ISoftDelete'. Sin embargo, si 'X' tiene una propiedad' IsDeleted', entonces, por definición, cualquier instanciación 'var i = new X()' devolverá '(i is ISoftDelete)' para que sea verdadera? Entonces 'X' implementa la interfaz (siguiendo su contrato), a pesar de que no hereda de la interfaz. ¿Esto usa la reflexión? ¿Hacer cosas como estas afectará en gran medida el rendimiento? –

0

uso el patrón de repositorio también con un código similar a LukLed de, pero utilizar la reflexión para ver si el IsHistorical propiedad es allí (ya que es un acuerdo sobre la convención de nomenclatura):

public class Repository<TEntityModel> where TEntityModel : EntityObject, new() 
{ 
     public void Delete(TEntityModel entity) 
     { 
      // see if the object has an "IsHistorical" flag 
      if (typeof(TEntityModel).GetProperty("IsHistorical") != null); 
      { 
       // perform soft delete 
       var historicalProperty = entity.GetType().GetProperty("IsHistorical"); 
       historicalProperty.SetValue(entity, true, null); 
      } 
      else 
      { 
       // perform real delete 
       EntityContext.DeleteObject(entity); 
      } 

      EntityContext.SaveChanges();     
     } 
} 

uso es entonces simplemente:

using (var fubarRepository = new Repository<Fubar>) 
{ 
    fubarRepository.Delete(someFubar); 
} 

por supuesto, en la práctica, a extender esto para permitir eliminaciones pasando PK en lugar de una entidad instancia, etc.

Cuestiones relacionadas