2010-09-27 22 views
9

Mi base de datos tiene una columna 'LastModifiedUser' en cada tabla en la que tengo la intención de recopilar el usuario conectado de una aplicación que realiza un cambio. No estoy hablando del usuario de la base de datos así que esencialmente esto es solo una cadena en cada entidad. Me gustaría encontrar una forma de predeterminar esto para cada entidad para que otros desarrolladores no tengan que recordar asignarla en cualquier momento que creen una instancia de la entidad.Entity Framework - Actividad de auditoría

así que algo como esto ocurriría:

using (EntityContext ctx = new EntityContext()) 
{ 
    MyEntity foo = new MyEntity(); 

    // Trying to avoid having the following line every time 
    // a new entity is created/added. 
    foo.LastModifiedUser = Lookupuser(); 

    ctx.Foos.Addobject(foo); 
    ctx.SaveChanges(); 
} 

Respuesta

23

No es una forma perfecta para lograr esto en la FE 4.0 mediante el aprovechamiento de ObjectStateManager

En primer lugar, es necesario crear una clase parcial para su ObjectContext y suscribirse a ObjectContext.SavingChanges Event. El mejor lugar para suscribirse a este evento es dentro del método OnContextCreated. Este método es llamado por el constructor del objeto contexto y las sobrecargas del constructor, que es un método parcial con ninguna aplicación:

partial void OnContextCreated() { 
    this.SavingChanges += Context_SavingChanges; 
} 


Ahora el código real que va a hacer el trabajo:

void Context_SavingChanges(object sender, EventArgs e) { 

    IEnumerable<ObjectStateEntry> objectStateEntries = 
     from ose 
     in this.ObjectStateManager.GetObjectStateEntries(EntityState.Added 
                 | EntityState.Modified) 
     where ose.Entity != null 
     select ose; 

    foreach (ObjectStateEntry entry in objectStateEntries) { 

     ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues 
       .DataRecordInfo.FieldMetadata; 

     FieldMetadata modifiedField = fieldsMetaData 
      .Where(f => f.FieldType.Name == "LastModifiedUser").FirstOrDefault(); 

     if (modifiedField.FieldType != null) { 

      string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name;      
      if (fieldTypeName == PrimitiveTypeKind.String.ToString()) { 
       entry.CurrentValues.SetString(modifiedField.Ordinal, Lookupuser()); 
      } 
     } 
    } 
} 

Código Explicación:
Este código ubica cualquier Agregado o Modificado entradas que tienen una propiedad LastModifiedUser y luego actualiza esa propiedad con el valor que proviene de su método personalizado Lookupuser().

En el bloque foreach, la consulta básicamente se centra en el CurrentValues de cada entrada. Luego, utilizando el método Dónde, se ve en los nombres de cada elemento FieldMetaData para esa entrada, recogiendo sólo aquellos cuya Name es LastModifiedUser. A continuación, la instrucción if verifica que la propiedad LastModifiedUser es un campo String; luego actualiza el valor del campo.

Otra manera de conectar este método (en lugar de suscribirse a SavingChanges de eventos) es reemplazando el ObjectContext.SaveChanges Method.

Por cierto, el código anterior pertenece a Julie Lerman de su libro Programming Entity Framework.


EDITAR para Auto Seguimiento POCO Implementación:

Si tiene POCOs de seguimiento auto continuación, lo que haría es que por primera vez cambiar la plantilla de T4 para llamar al método OnContextCreated(). Si observa su archivo ObjectContext.tt, hay un método Initialize() que todos los constructores llaman, por lo tanto, es un buen candidato para llamar a nuestro método OnContextCreated(), así que todo lo que tenemos que hacer es cambiar ObjectContext.tt presentar la siguiente manera:

private void Initialize() 
{ 
    // Creating proxies requires the use of the ProxyDataContractResolver and 
    // may allow lazy loading which can expand the loaded graph during serialization. 
    ContextOptions.ProxyCreationEnabled = false; 
    ObjectMaterialized += new ObjectMaterializedEventHandler(HandleObjectMaterialized); 
    // We call our custom method here: 
    OnContextCreated(); 
} 

Y esto hará que nuestra OnContextCreated() para ser llamados a la creación del Contexto.

Ahora bien, si usted pone sus POCOs detrás de la frontera de servicio, entonces significa que el ModifiedUserName debe venir con el resto de los datos de su consumidor de servicios WCF. Usted puede exponga este LastModifiedUser propiedad para que actualice o si se almacena en otra propiedad y desea actualizar LastModifiedUser de esa propiedad, entonces se puede modificar el segundo código como sigue:


foreach (ObjectStateEntry entry in objectStateEntries) { 

    ReadOnlyCollection fieldsMetaData = entry.CurrentValues 
      .DataRecordInfo.FieldMetadata; 

    FieldMetadata sourceField = fieldsMetaData 
      .Where(f => f.FieldType.Name == "YourPropertyName").FirstOrDefault();    

    FieldMetadata modifiedField = fieldsMetaData 
     .Where(f => f.FieldType.Name == "LastModifiedUser").FirstOrDefault(); 

    if (modifiedField.FieldType != null) { 

     string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name; 
     if (fieldTypeName == PrimitiveTypeKind.String.ToString()) { 
      entry.CurrentValues.SetString(modifiedField.Ordinal, 
        entry.CurrentValues[sourceField.Ordinal].ToString()); 
     } 
    } 
} 


Espero que esto ayude.

+0

Gracias por la respuesta. Había analizado este patrón antes, pero nunca pude activar el evento onContextCreated. – Biggle10

+1

No hay problema. Como dije, OnContextCreated no es un evento, simplemente es un método parcial sin implementación que es llamado por el constructor del objeto de contexto y el constructor se sobrecarga. Todo lo que necesitas hacer es crear una clase parcial para tu contexto y poner mi primer fragmento de código dentro de él. ¡Hazlo y cuéntame cómo te funciona, y si todavía no funciona, lo haremos funcionar! –

+0

En realidad, estaba pensando en esto de la manera incorrecta y, por lo tanto, lo describí mal. Permítanme refinar mi pregunta ... Utilizamos entidades de auto-seguimiento y se sirven a través de un servicio de WCF al frente. Cuando se devuelven, nos volvemos a unir al contexto y guardamos. Si se modificaron mientras estaban en posesión del cliente, el lastModifiedUser debe reflejar eso. El servidor no tiene acceso para saber quién es el usuario actual, por lo que no puede ser el responsable de configurarlo. Supongo que en teoría quiero hacer algo así como un evento/método parcial creado a nivel de entidad. – Biggle10