Entity Framework 6.2.0 - DbContext, который автоматически сохраняет, какой пользователь создал или обновил значение DbSet - PullRequest
0 голосов
/ 28 декабря 2018

Как видно из заголовка, нам нужно сохранить, какой пользователь обновил или создал конкретное значение.Ранее нам нужно было только сохранить 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;
}

1 Ответ

0 голосов
/ 04 февраля 2019

Решили решить это так:

public class CatalogServiceContext : DbContext
{
    public ICurrentUser CurrentUser;

    [Obsolete("Use CatalogServiceContextUserWrapper instead")]
    public CatalogServiceContext() : base("name=CatalogContext")
    {

    }

    ...


public class CatalogServiceContextUserWrapper
{
    public CatalogServiceContext Context;

    public CatalogServiceContextUserWrapper(CatalogServiceContext context, ICurrentUser currentUser)
    {
        context.CurrentUser = currentUser;
        this.Context = context;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...