2009-09-21 23 views
15

He estado buscando algunos enlaces de auditoría con Entity Framework. Muchos de ellos muestran comparaciones de valores antiguos/nuevos. Esto funciona muy bien para un seguimiento de auditoría, pero estoy buscando objetos de instantáneas.Historial de instantáneas con Entity Framework

Por ejemplo ... Digamos que tengo una aplicación que administra productos. Un producto tiene múltiples atributos y otros objetos asociados. Digamos que cambio un objeto 10 veces. Digamos también que es importante que pueda ver las pantallas de esos cambios de objeto (no una pista de auditoría, sino cómo se veía la pantalla en un formato de solo lectura). Lo que me interesa es poder recuperar el objeto de producto original de EF (con todos los datos asociados) para todos los 10 cambios (dependiendo de lo que quiera ver) y utilizarlo para enlazar a mi pantalla.

Si uso SQL Server, ¿qué tipo debo usar para el objeto serializado hoy en día (XML, blob, etc.)? ¿Tiene sentido hacer esto?

+0

Agregaré una recompensa para obtener una respuesta más detallada, por favor :) –

+1

@AnynameDonotcare para cada tabla para la que desee almacenar el historial: 1) agregar la columna Versión (puede ser hora, puede incrementarse el contador). 2) agregue otra tabla con todas las mismas propiedades que la tabla principal (pero mejor expanda las claves externas). 3) Antes de la activación de la actualización, si la columna Versión ha cambiado, copie los valores antiguos en esta tabla de versiones. 4) Beneficio. – Evk

+0

@Evk: estaré muy agradecido si pudiera agregar una respuesta detallada con un ejemplo simple (usando 'EF') podría usarse como una base para la aplicación empresarial (sobre la eliminación de acciones de acción y mm) –

Respuesta

11

Veamos. Usted tiene el requisito de tomar un gráfico de objetos y serializarlo en la base de datos en un formato que le permitirá materializarlo más adelante. Creo que hay herramientas que hacen exactamente esto. Uno de ellos, me parece, es el Marco de la Entidad.

Lo que quieres hacer es algo muy común. Considera un motor wiki. La wiki necesita una revisión de consejos que todos vean, además de revisiones posteriores de cada documento. El wiki también debe poder mostrar una revisión posterior de la misma manera que se muestra una revisión de punta. Por lo tanto, se debe usar el mismo formato de almacenamiento para ambos.

Propongo que permita que todos los tipos de entidad sean versionados. Cuando edite un tipo de entidad, editará la revisión de punta y almacenará una revisión posterior que contenga los valores anteriores. (La razón por la que edita la revisión de sugerencia en lugar de insertar una nueva sugerencia es porque otros objetos, que actualmente no se materializan en ObjectContext, pueden contener enlaces a la sugerencia que desea conservar como enlaces a la sugerencia, en lugar de enlaces a la revisión posterior).

Si es necesario, puede crear particiones en las tablas de SQL Server para que las revisiones posteriores se almacenen en un grupo de archivos diferente. Esto le permitiría realizar copias de seguridad de las revisiones de sugerencias y revisiones posteriores por separado.

+0

Muy buena idea ... – RailRhoad

+0

'Propongo que permitas que todos tus tipos de entidad sean versionados. ¿Podrías aclarar más por favor a qué te refieres con esto? –

3

En un proyecto que construí recientemente utilizamos enchufamos al método SaveChanges en la clase DbContext. Esto nos dio acceso a una instancia de la clase ChangeTracker. Llamar al ChangeTracker.Entries() le da acceso a una lista de DbEntityEntry. DbEntityEntry tiene las siguientes propiedades y métodos interesantes:

  • State - es el objeto recién creado, modificado o el borrado
  • Entity - una copia del objeto tal y como está
  • CurrentValues - una enumeración de la editado valorada
  • OriginalValues - una enumeración de los valores originales

Hemos creado un conjunto de POCOs para conjuntos de cambios y cambios a los que podríamos acceder a través de EF. Esto permitió a nuestros usuarios ver los cambios de nivel de campo junto con las fechas y los usuarios responsables.

3

En primer lugar es necesario agregar un conjunto de propiedades a las tablas:

  • Versión - hora de la última modificación (también puede ser autoincremental contador en lugar de tiempo).
  • LastModifiedBy - referencia al usuario que realizó la última modificación (si la almacena).

Luego tiene varias opciones sobre cómo almacenar su historial de versiones. Puede

  1. Cree una nueva tabla para cada una de las tablas principales para las que desea almacenar el historial. Las tablas de historial tendrán los mismos campos que la tabla principal, pero las claves primarias y externas no se aplicarán. Para cada clave externa también se creó la versión de la tienda de la entrada referenciada en la versión de tiempo.

  2. O puede serializar todo lo interesante sobre su entidad y almacenar todos los blobs serializados para todas las entidades que quiera versionar en una tabla de historial global (personalmente prefiero el primer acercamiento).

¿Cómo llenas tus tablas de historial? Mediante actualización y eliminación de desencadenantes.

  • En la activación de la actualización de su entidad, copie todos los valores anteriores en la tabla del historial. Para cada clave externa, también copie la versión actual de la entidad a la que se hace referencia.
  • En supresión de eliminación: básicamente, haga lo mismo.

Tenga en cuenta que cada vez más sistemas modernos NO eliminan nada. Solo marca cosas como eliminadas. Si desea seguir este patrón (que tiene varios beneficios), en lugar de eliminar agregue el indicador IsDeleted a sus entidades (por supuesto, debe filtrar entidades eliminadas en todas partes).

¿Cómo ves tu historial? Solo use la tabla de historial, ya que tiene todas las mismas propiedades que la tabla principal, no debería ser un problema. Pero, al expandir claves externas, asegúrese de que la Versión de entidad referenciada sea la misma que la que almacena en su tabla de historial. Si no es así, debe ir a la tabla de Historial de esa entidad a la que se hace referencia y tomar los valores allí. De esta forma, siempre tendrá una instantánea de cómo se veía la entidad en ESE momento, incluidas todas las referencias.

Además de todo lo anterior, también puede restaurar el estado de su entidad a cualquier versión anterior.

Tenga en cuenta que esta implementación, aunque es fácil, puede consumir algo de espacio, ya que almacena instantánea, no solo se están realizando cambios. Si solo desea almacenar cambios, en el desencadenador de actualización puede detectar qué campos se han cambiado, serializarlos y almacenarlos en la tabla de historial global. De esa manera, al menos puede mostrar en la interfaz de usuario qué ha cambiado y por quién (aunque puede tener problemas para volver a una versión anterior).

+0

¿Por qué prefiere el primer acercamiento al segundo, aunque incluye más datos redundantes y requiere más almacenamiento de db? Los factores desencadenantes causan problemas de rendimiento además de que quiero manejar todas estas operaciones a través de 'EF'. ¿Podría proporcionar un ejemplo simple (dos tablas)? –

+0

Oí la primera vez que "los desencadenantes causan problemas de rendimiento". Los disparadores por sí solos no pueden causarlos, solo si haces algo de lógica pesada dentro. Con todo, puede hacer eso en EF exactamente de la misma manera: solo en su controlador de actualización copie todos los valores actuales primero en la tabla de historial (no estoy seguro de qué ejemplo puedo proporcionar aquí). Supongo que sabe cómo copiar valores de una entidad a otro). Prefiero el primer acercamiento porque es más simple y claro, además permite una restauración fácil a cualquier versión anterior. Además de eso, permite _search_ en la historia (que podría ser más difícil de hacer si serializas). – Evk

+0

si quiero manejar la lógica del disparador a través de 'EF', ¿debería estar en la transacción? –