Переопределение 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();
}
Ответы
Ответ 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);