6

似乎直接覆盖 EF 中的 SaveChanges 以添加审核记录器。请参阅下面的 ApplyAuditLogging 方法来设置审计属性(created、createdby、updated、updatedby)。

   public override int SaveChanges()
    {
        var autoDetectChanges = Configuration.AutoDetectChangesEnabled;

        try
        {
            Configuration.AutoDetectChangesEnabled = false;
            ChangeTracker.DetectChanges();
            var errors = GetValidationErrors().ToList();
            if(errors.Any())
            {
                throw new DbEntityValidationException("Validation errors were found during save: " + errors);
            }

            foreach (var entry in ChangeTracker.Entries().Where(e => e.State == EntityState.Added || e.State == EntityState.Modified))
            {
                ApplyAuditLogging(entry);
            }

            ChangeTracker.DetectChanges();

            Configuration.ValidateOnSaveEnabled = false;

            return base.SaveChanges();
        }
        finally
        {
            Configuration.AutoDetectChangesEnabled = autoDetectChanges;
        }
    }

    private static void ApplyAuditLogging(DbEntityEntry entityEntry)
    {

        var logger = entityEntry.Entity as IAuditLogger;
        if (logger == null) return;

        var currentValue = entityEntry.Cast<IAuditLogger>().Property(p => p.Audit).CurrentValue;
        if (currentValue == null) currentValue = new Audit();
        currentValue.Updated = DateTime.Now;
        currentValue.UpdatedBy = "???????????????????????";
        if(entityEntry.State == EntityState.Added)
        {
            currentValue.Created = DateTime.Now;
            currentValue.CreatedBy = "????????????????????????";
        }
    }

问题是如何让 windows 用户登录/用户名设置对象的 UpdatedBy 和 CreatedBy 属性?因此我不能使用它!

另外,在另一种情况下,我想自动将新的 CallHistory 记录添加到我的联系人中;每当修改联系人时,都需要在子表 CallHistory 中添加一条新记录。所以我在 Repository 的 InsertOrUpdate 中做到了,但感觉很脏,如果我能在更高的级别上做到这一点会很好,因为现在我必须从数据库中设置当前用户。同样这里的问题是我需要从数据库中获取用户以创建 CallHistory 记录(SalesRep = User)。

我的存储库中的代码现在做了 2 件事,1,它在创建或更新对象时创建了一个审计条目,2,它还在更新联系人时创建了一个 CallHistory 条目:

ContactRepository.SetCurrentUser(User).InsertOrUpdate(contact)

为了让用户在存储库上下文中:

    var prop = typeof(T).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);

    if (prop.GetValue(entity, null).ToString() == "0")
    {
        // New entity
        _context.Set<T>().Add(entity);
        var auditLogger = entity as IAuditLogger;
        if (auditLogger != null)
            auditLogger.Audit = new Audit(true, _principal.Identity.Name);
    }
    else
    {
        // Existing entity
        _context.Entry(entity).State = EntityState.Modified;
        var auditLogger = entity as IAuditLogger;
        if (auditLogger != null && auditLogger.Audit != null)
        {
            (entity as IAuditLogger).Audit.Updated = DateTime.Now;
            (entity as IAuditLogger).Audit.UpdatedBy = _principal.Identity.Name;
        }

        var contact = entity as Contact;
        if (_currentUser != null)
            contact.CallHistories.Add(new CallHistory
                {
                    CallTime = DateTime.Now,
                    Contact = contact,
                    Created = DateTime.Now,
                    CreatedBy = _currentUser.Logon,
                    SalesRep = _currentUser
                });
    }
}

有没有办法以某种方式将 Windows 用户注入 DbContext 中的 SaveChanges 覆盖,还有一种方法可以根据 Windows 登录 ID 从数据库中获取用户,以便我可以在 CallHistory 上设置 SalesRep(参见上面的代码)?

这是我在 MVC 应用程序上对控制器的操作:

[HttpPost]
public ActionResult Create([Bind(Prefix = "Contact")]Contact contact, FormCollection collection)
{
    SetupVOs(collection, contact, true);
    SetupBuyingProcesses(collection, contact, true);

    var result = ContactRepository.Validate(contact);

    Validate(result);

    if (ModelState.IsValid)
    {
        ContactRepository.SetCurrentUser(User).InsertOrUpdate(contact);
        ContactRepository.Save();
        return RedirectToAction("Edit", "Contact", new {id = contact.Id});
    }

    var viewData = LoadContactControllerCreateViewModel(contact);

    SetupPrefixDropdown(viewData, contact);

    return View(viewData);
}
4

3 回答 3

6

好吧,简单而懒惰的方法是从您的审计代码中简单地访问 HttpContext.Current.User.Identity.Name 。但是,这将创建对 System.Web.* 的依赖关系,如果您有一个分层良好的应用程序,这可能不是您想要的(如果您使用实际的单独层,它将无法工作)。

一种选择是,而不是覆盖 SaveChanges,只需创建一个使用您的用户名的重载。然后你做你的工作,然后调用真正的 SaveChanges。缺点是有人可能会错误地(或故意地)调用 SaveChanges() (真实的)并绕过审计。

更好的方法是简单地将 _currentUser 属性添加到您的 DbContext 并使用构造函数将其传入。然后,当您创建上下文时,您只需在那时将用户传入。不幸的是,您不能真正从构造函数中查找数据库中的用户。

但是您可以简单地保存 ContactID 并添加它而不是整个联系人。您的联系人应该已经存在。

于 2012-09-24T22:51:11.290 回答
1

我知道这是一个迟到的答案,但我只是偶然发现了这个问题。我有一个非常相似的用例。我们是这样做的:

var auditUsername = Current.User.Identity.Name;
var auditDate = DateTime.Now;

现在的班级:

public class Current
    {
        public static IPrincipal User
        {
            get
            {
                return System.Threading.Thread.CurrentPrincipal;
            }
            set
            {
                System.Threading.Thread.CurrentPrincipal = value;
            }

        }
    }

这将返回进程的 windows 用户,或在 ASP.NET 应用程序中登录的用户。阅读更多:http ://www.hanselman.com/blog/SystemThreadingThreadCurrentPrincipalVsSystemWebHttpContextCurrentUserOrWhyFormsAuthenticationCanBeSubtle.aspx

于 2013-07-18T07:50:06.483 回答
0

我认为您可能正在跨越一些关注边界的分离。存储库模式用于分离您的业务逻辑、数据库映射和数据库 crud 操作。应用程序应该关注登录的用户,存储库应该只关注保存数据。我建议不要在您的存储库中引用 HttpContext,因为如果这样做,那么您的存储库只能由 Web 应用程序使用。如果您试图抽象出这种元数据的数量,请在您的应用程序中进行...例如在基本控制器或其他东西中。

于 2013-07-10T11:57:18.923 回答