首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实体框架6-审计日志

实体框架6-审计日志
EN

Stack Overflow用户
提问于 2014-08-11 14:11:09
回答 1查看 4.8K关注 0票数 1

我对实体框架6.0下的审计日志有疑问

我已经实现了它,但当我更新或插入时,它确实会减慢速度。

在其他应用程序(不使用EF)中,我使用审计信息创建内存XML,然后将其发送到SQL Server中的存储过程,以便插入审计日志,速度惊人。你没意识到你在做审计记录。

我想知道是否有其他方法或最佳做法。

我的实际代码如下所示:

在我的DbContext中,我重写了SaveChanges()方法:

代码语言:javascript
复制
public override int SaveChanges() {
    throw new InvalidOperationException("User ID and Session ID must be provided");
}

然后实现我的custon SaveChanges()方法。

代码语言:javascript
复制
public int SaveChanges(int userId, Guid sessionId, bool saveAuditLog) {
    if (saveAuditLog && userId > 0) {
        // this list will exclude entities for auditing
        List < string > excludeEntities = new List < string > ();
        excludeEntities.Add("SecuritySession");
        excludeEntities.Add("SecurityUserActivityLog");

        this.ObjectContext = ((IObjectContextAdapter) this).ObjectContext;
        // Get all Added/Deleted/Modified entities (not Unmodified or Detached)
        foreach(var ent in this.ChangeTracker.Entries().Where(p => p.State == System.Data.Entity.EntityState.Added || p.State == System.Data.Entity.EntityState.Deleted || p.State == System.Data.Entity.EntityState.Modified)) {
            // security session is not Auditable 

            if (!excludeEntities.Contains(ent.Entity.GetType().Name)) {
                // For each changed record, get the audit record entries and add them
                foreach(AuditLog x in GetAuditRecordsForChange(ent, userId, sessionId)) {
                    this.AuditLogs.Add(x);
                }
            }

        }
    }

最后,我有一个方法GetAuditRecordsForChange()来完成审计工作。

代码语言:javascript
复制
 private List < AuditLog > GetAuditRecordsForChange(DbEntityEntry dbEntry, int userId, Guid sessionId) {
        Type entityType = dbEntry.Entity.GetType();
        if (entityType.BaseType != null && entityType.Namespace == "System.Data.Entity.DynamicProxies")
            entityType = entityType.BaseType;

        string entityTypeName = entityType.Name;

        string[] keyNames;

        MethodInfo method = Data.Helpers.EntityKeyHelper.Instance.GetType().GetMethod("GetKeyNames");
        keyNames = (string[]) method.MakeGenericMethod(entityType).Invoke(Data.Helpers.EntityKeyHelper.Instance, new object[] {
            this
        });

        List < AuditLog > result = new List < AuditLog > ();

        DateTime changeTime = DateTime.Now;

        // Get table name (if it has a Table attribute, use that, otherwise get the pluralized name)
        string tableName = entityTypeName;

        if (dbEntry.State == System.Data.Entity.EntityState.Added) {
            // For Inserts, just add the whole record
            // If the entity implements IDescribableEntity, use the description from Describe(), otherwise use ToString()

            foreach(string propertyName in dbEntry.CurrentValues.PropertyNames) {
                result.Add(new AuditLog() {
                    AuditLogId = Guid.NewGuid(),
                        UserId = userId,
                        SessionId = sessionId,
                        EventDateUTC = changeTime,
                        EventType = "A", // Added
                        TableName = tableName,
                        RecordId = dbEntry.CurrentValues.GetValue < object > (keyNames[0]).ToString(),
                        ColumnName = propertyName,
                        NewValue = dbEntry.CurrentValues.GetValue < object > (propertyName) == null ? null : dbEntry.CurrentValues.GetValue < object > (propertyName).ToString()
                });
            }
        } else if (dbEntry.State == System.Data.Entity.EntityState.Deleted) {
            // Same with deletes, do the whole record, and use either the description from Describe() or ToString()
            result.Add(new AuditLog() {
                AuditLogId = Guid.NewGuid(),
                    UserId = userId,
                    SessionId = sessionId,
                    EventDateUTC = changeTime,
                    EventType = "D", // Deleted
                    TableName = tableName,
                    RecordId = dbEntry.OriginalValues.GetValue < object > (keyNames[0]).ToString(),
                    ColumnName = "*ALL",
                    NewValue = (dbEntry.OriginalValues.ToObject() is IDescribableEntity) ? (dbEntry.OriginalValues.ToObject() as IDescribableEntity).Describe() : dbEntry.OriginalValues.ToObject().ToString()
            });
        } else if (dbEntry.State == System.Data.Entity.EntityState.Modified) {
            foreach(string propertyName in dbEntry.OriginalValues.PropertyNames) {
                // For updates, we only want to capture the columns that actually changed
                if (!object.Equals(dbEntry.OriginalValues.GetValue < object > (propertyName), dbEntry.CurrentValues.GetValue < object > (propertyName))) {
                    result.Add(new AuditLog() {
                        AuditLogId = Guid.NewGuid(),
                            UserId = userId,
                            SessionId = sessionId,
                            EventDateUTC = changeTime,
                            EventType = "M", // Modified
                            TableName = tableName,
                            RecordId = dbEntry.OriginalValues.GetValue < object > (keyNames[0]).ToString(),
                            ColumnName = propertyName,
                            OriginalValue = dbEntry.OriginalValues.GetValue < object > (propertyName) == null ? null : dbEntry.OriginalValues.GetValue < object > (propertyName).ToString(),
                            NewValue = dbEntry.CurrentValues.GetValue < object > (propertyName) == null ? null : dbEntry.CurrentValues.GetValue < object > (propertyName).ToString()
                    });
                }
            }
        }
        // Otherwise, don't do anything, we don't care about Unchanged or Detached entities

        return result;
    }

    // Call the original SaveChanges(), which will save both the changes made and the audit records
    return base.SaveChanges();
}
EN

回答 1

Stack Overflow用户

发布于 2014-08-12 23:30:26

您最大的性能问题似乎是对ChangeTracker.Entries()和DbSet.Add()的调用都在内部触发DetectChanges()方法,该方法与DbContext实例主动跟踪的实体数量成正比。我怀疑对foreach循环中的.Add()的调用是主要的罪魁祸首,因为它多次调用该方法。因为要在foreach循环中添加AuditLog实体,所以对DetectChanges()的每次调用都要慢一些,因为DbContext实例正在跟踪更多的实体。

有关更多详细信息,请参阅http://blog.oneunicorn.com/2012/03/11/secrets-of-detectchanges-part-2-when-is-detectchanges-called-automatically/

调用DbSet.Add()或DbSet.Remove()适当地设置实体的状态(不调用DetectChanges()),但更新实体的实体状态不会自动更新(这是DetectChanges()完成的一件事)。您需要在方法开始时对DetectChanges()进行单个调用(隐式或显式),以确保所有更新的实体都被标记为已更新。注意,除非显式关闭,否则基SaveChanges()方法会自动调用DetectChanges()。

此外,反复的反思性呼吁也可能没有帮助。我建议通过使用System.Diagnostics.Stopwatch或其他更复杂的方法来具体确定瓶颈来分析这种方法。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25245351

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档