11

我的数据库在每个表上都有一个“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();
}
4

2 回答 2

25

通过利用ObjectStateManager

在 EF 4.0 中有一个完美的方法来完成此操作。 首先,您需要为 ObjectContext 创建一个分部类并订阅 ObjectContext.SavingChanges 事件。订阅此事件的最佳位置是在 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。然后,使用Where方法,它查看该条目的每个FieldMetaData项的名称,只选择那些NameLastModifiedUser的项。接下来,if 语句验证LastModifiedUser属性是否为String

场地; 然后它更新字段的值。

连接此方法的另一种方法(而不是订阅SavingChanges事件)是覆盖ObjectContext.SaveChanges 方法

顺便说一句,上面的代码属于Julie Lerman,来自她的Programming Entity Framework书。


自我跟踪 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,那么您可以修改第二个代码,如下所示:


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


希望这可以帮助。

于 2010-09-27T23:54:54.170 回答
2

现在有一个nuget 包https ://www.nuget.org/packages/TrackerEnabledDbContext

Githubhttps ://github.com/bilal-fazlani/tracker-enabled-dbcontext

于 2014-11-02T08:44:40.927 回答