Подтвердить что ты не робот

Переопределение SaveChanges и установка ModifiedDate, но как установить ModifiedBy?

У меня есть веб-приложение ASP.NET MVC3 с уровнями пользовательского интерфейса, бизнеса (сущности) и данных (DbContext). Сначала я использую Entity Framework 4.1 Code First. Прямо сейчас я переопределяю DbContext.SaveChanges() в слое данных, чтобы я мог установить ModifiedDate для всех изменений, внесенных в любые объекты сущности, реализующие мой интерфейс IAuditable. У меня есть статический DateProvider класс и метод (GetCurrentDate), который возвращает DateTime.Now (если я не запускаю тест, и в этом случае он возвращает все, что я ему сказал).

Я хотел бы автоматически установить свойство ModifiedBy текущему пользователю. Каков наилучший способ сделать это? Есть ли что-то, что встроено в структуру, которая позволит мне получить доступ к этой информации или мне нужно установить что-то вроде класса DateProvider? Это среда Active Directory, и мы используем WindowsAuthentication в IIS.

Вот мой код SaveChanges:

public override int SaveChanges()
{
    var changeSet = ChangeTracker.Entries<IAuditable>();

    if (changeSet != null)
    {
        foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
        {
            entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
        }
    }
    return base.SaveChanges();
}
4b9b3361

Ответ 1

Вы можете использовать HttpContext.Current.User.Identity.Name, чтобы получить имя текущего пользователя.

public override int SaveChanges()
{
    var changeSet = ChangeTracker.Entries<IAuditable>();

    if (changeSet != null)
    {
        foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
        {
            entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
            entry.Entity.ModifiedBy = HttpContext.Current.User.Identity.Name;
        }
    }
    return base.SaveChanges();
}

Лучше всего это сделать, чтобы использовать инсталляцию конструктора, чтобы передать текущего пользователя в контекст

public class MyContext : DbContext
{
    public MyContext(string userName)
    {
        UserName = userName;
    }

    public string UserName
    {
        get; private set;
    }

    public override int SaveChanges()
    {
       var changeSet = ChangeTracker.Entries<IAuditable>();

       if (changeSet != null)
       {
          foreach (var entry in changeSet.Where(c => c.State != EntityState.Unchanged))
          {
              entry.Entity.ModifiedDate = DateProvider.GetCurrentDate();
              entry.Entity.ModifiedBy = UserName;
          }
       }
       return base.SaveChanges();
   }
}

Ответ 2

Я также хотел автоматизировать популяцию полей аудита в моем приложении MVC 4/Entity Framework 5. Я использовал информацию из ответа @Eranga и этого блога: http://lourenco.co.za/blog/2013/07/audit-trails-concurrency-and-soft-deletion-with-entity-framework/, чтобы этот подход работал у меня с Ninject - публикация в случае, ценная для любого еще:

Создал интерфейс и абстрактный класс:

public interface IAuditableEntity {
    DateTime? CreatedDate { get; set; }
    string CreatedBy { get; set; }
    DateTime? LastModifiedDate { get; set; }
    string LastModifiedBy { get; set; }
}

public abstract class AuditableEntity:IAuditableEntity {
    public DateTime? CreatedDate { get; set; }
    public string CreatedBy { get; set; }
    public DateTime? LastModifiedDate { get; set; }
    public string LastModifiedBy { get; set; }
}

Использовал их в моих сущностях:

public class DataEntity : AuditableEntity {
    public int DataEntityID { get; set; }
    ...
}

Добавлен конструктор в MyDbContext, который принял HttpContext и переопределит SaveChanges:

public EFDbContext(HttpContext context) {
    _context = context;
}

public HttpContext _context {
    get;
    private set;
}

public override int SaveChanges() {
    DateTime currentDateTime = DateTime.Now;

    foreach (var auditableEntity in ChangeTracker.Entries<IAuditableEntity>()) {
        if (auditableEntity.State == EntityState.Added || auditableEntity.State == EntityState.Modified) {
            auditableEntity.Entity.LastModifiedDate = currentDateTime;
            switch (auditableEntity.State) {
                    case EntityState.Added:
                        auditableEntity.Entity.CreatedDate = currentDateTime;
                        auditableEntity.Entity.CreatedBy = _context.User.Identity.Name;
                        break;
                    case EntityState.Modified:
                        auditableEntity.Entity.LastModifiedDate = currentDateTime;
                        auditableEntity.Entity.LastModifiedBy = _context.User.Identity.Name;
                        if (auditableEntity.Property(p => p.CreatedDate).IsModified || auditableEntity.Property(p => p.CreatedBy).IsModified) {
                            throw new DbEntityValidationException(string.Format("Attempt to change created audit trails on a modified {0}", auditableEntity.Entity.GetType().FullName));
                        }
                        break;
                }
            }
        }
        return base.SaveChanges();
    }

Наконец - нужно иметь DbContext для каждого запроса и передать HttpContext, как показано ниже, на основе этого ответа: fooobar.com/info/245759/... - обратите внимание, что, поскольку MyDbContext теперь является областью запроса, Хранилища должны быть также.

kernel.Bind<IDataRepository>()
      .To<EFDataRepository>()
      .InRequestScope();

kernel.Bind<MyDbContext>().ToSelf()
      .InRequestScope()
      .WithConstructorArgument("context", ninjectContext=>HttpContext.Current);