Как видно из заголовка, нам нужно сохранить, какой пользователь обновил или создал конкретное значение.Ранее нам нужно было только сохранить CreatedDate
и UpdatedDate
.Это было довольно легко, у нас были все наши объекты, наследуемые от абстрактного базового класса EntityBase
, который реализовал интерфейс IEntity
, который имел эти значения.DbContext
SaveChanges
затем был переопределен, и если конкретный объект реализовал IEntity
, значения были установлены на основе EntityState.Added
или EntityState.Modified
.
interface IEntity
{
DateTime CreatedDate { get; set; }
string CreatedBy { get; set; }
DateTime UpdatedDate { get; set; }
string UpdatedBy { get; set; }
}
public abstract class EntityBase<T1>: IEntity
{
public T1 Id { get; set; }
public virtual DateTime CreatedDate { get; set; }
public virtual string CreatedBy { get; set; }
public virtual DateTime UpdatedDate { get; set; }
public virtual string UpdatedBy { get; set; }
}
Однако новое требование заключается в том, что нам также необходимо сохранить, какой пользователь внес изменения.Простым решением было бы добавить пользователя через конструктор и не иметь пустого конструктора.Однако это создает проблему при использовании миграций среди других:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<CatalogServiceContext, Configuration>());
var migrator = new DbMigrator(new Configuration());
Целевой контекст 'CatalogService.Data.Context.CatalogServiceContext' не может быть создан.Добавьте конструктор по умолчанию или предоставьте реализацию IDbContextFactory.
public class CatalogServiceContext : DbContext
{
private readonly string _currentUser;
public CatalogServiceContext(string currentUser) : base("name=CatalogContext")
{
_currentUser = currentUser;
}
public override int SaveChanges()
{
var now = DateTime.Now;
foreach (var changedEntity in ChangeTracker.Entries())
{
if (changedEntity.Entity is IEntity entity)
{
switch (changedEntity.State)
{
case EntityState.Added:
entity.CreatedDate = now;
entity.CreatedBy = _currentUser;
entity.UpdatedDate = now;
entity.UpdatedBy = _currentUser;
break;
case EntityState.Modified:
Entry(entity).Property(x => x.CreatedDate).IsModified = false;
Entry(entity).Property(x => x.CreatedBy).IsModified = false;
entity.UpdatedDate = now;
entity.UpdatedBy = _currentUser;
break;
}
}
}
return base.SaveChanges();
}
}
Я пытался реализовать IDbContextFactory
, но у него похожая проблема.
Тип фабрики контекста CatalogService.Data.Context.CatalogServiceContextFactory не имеет открытого конструктора без параметров.Либо добавьте открытый конструктор без параметров, создайте реализацию IDbContextFactory в сборке контекста, либо зарегистрируйте фабрику контекста с помощью DbConfiguration.
public class CatalogServiceContextFactory : IDbContextFactory<CatalogServiceContext>
{
private readonly string _currentUser;
public CatalogServiceContextFactory(string currentUser)
{
_currentUser = currentUser;
}
public CatalogServiceContext Create()
{
return new CatalogServiceContext(_currentUser);
}
}
Обходной путь - это два метода в EntityBase
, которые необходимо вызватькаждый раз, когда значение обновляется или создается.Это работает, но, на мой взгляд, лучшим решением по-прежнему является принудительный пользователь в конструкторе.Есть ли кто-нибудь еще с аналогичным спросом, который решил это?Я хотел бы решить ее в DbContext
и не использовать абстракцию репозитория для каждого DbSet
.
public virtual EntityBase<T1> SetCreateFields(string currentUser)
{
CreatedBy = currentUser;
UpdatedBy = currentUser;
return this;
}
public virtual EntityBase<T1> SetUpdateFields(string currentUser)
{
UpdatedBy = currentUser;
return this;
}