Entity Framework - Аудиторская деятельность - PullRequest
11 голосов
/ 28 сентября 2010

В моей базе данных есть столбец «LastModifiedUser» в каждой таблице, в котором я собираюсь собрать зарегистрированного пользователя из приложения, которое вносит изменения.Я не говорю о пользователе базы данных, так что по сути это просто строка для каждой сущности.Я хотел бы найти способ установить по умолчанию это для каждой сущности, чтобы другие разработчики не могли помнить, чтобы назначать ее каждый раз, когда они создают экземпляр сущности.

Так что-то подобное может произойти:

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();
}

Ответы [ 2 ]

25 голосов
/ 28 сентября 2010

Существует отличный способ сделать это в EF 4.0, используя ObjectStateManager

Во-первых, вам нужно создать частичный классдля вашего ObjectContext и подпишитесь на ObjectContext.SavingChanges Event .Лучшее место для подписки на это событие - внутри метода OnContextCreated.Этот метод вызывается конструктором объекта контекста и перегружает конструктор, который является частичным методом без реализации:

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


Теперь фактический код, который будет выполнять эту работу:

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());
            }
        }
    }
}

Объяснение кода :
Этот код находит любые добавленные или измененные записи, которые имеют свойство LastModifiedUser , а затем обновляет ихсвойство со значением, полученным из вашего пользовательского Lookupuser () метода.

В блоке foreach запрос в основном детализирует CurrentValues ​​ каждой записи.Затем, используя метод Где , он просматривает имена каждого элемента FieldMetaData для этой записи, выбирая только те, чьи Имя равно LastModifiedUser .Затем оператор if проверяет, что свойство LastModifiedUser является полем String ;затем он обновляет значение поля.

Другой способ подключить этот метод (вместо подписки на событие SavingChanges ) - переопределить метод ObjectContext.SaveChanges .

Кстати, приведенный выше код принадлежит Джули Лерман из ее Programming Entity Framework book.


РЕДАКТИРОВАТЬ для реализации POCO с самотрекингом:

Если у вас есть POCO с самотрекингом, то я бы сначала изменил шаблон T4 для вызоваМетод OnContextCreated ().Если вы посмотрите на файл ObjectContext.tt, есть метод Initialize () , который вызывается всеми конструкторами, поэтому хороший кандидат для вызова нашего метода OnContextCreated (), поэтому все, что нам нужно сделать, этоизмените файл ObjectContext.tt следующим образом:

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();
}

И это вызовет вызов OnContextCreated () при создании контекста.

Теперь, если вы поместите свои POCO за границу службы, это означает, что ModifiedUserName должно поставляться с остальными данными от вашего потребителя службы WCF.Вы можете либо предоставить им это свойство LastModifiedUser для обновления, либо, если оно сохраняется в другом свойстве, и вы хотите обновить LastModifiedUser из этого свойства, то вы можете изменить 2-й код следующим образом:


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());
        }
    }
}


Надеюсь, это поможет.

2 голосов
/ 02 ноября 2014
...