EF Core 更改跟踪 - 原始值和更改值的问题

Bha*_*rat 5 c# repository-pattern asp.net-core ef-core-2.0

我使用 .net core 2.0 和 EF core 2.0 配置了 Net core API。它包含存储库模式架构。

现在,我尝试使用 EF 更改跟踪器为每个保存更改实现审核日志。

我的问题:每当我尝试添加编辑/修改端点的日志时,原始值和当前值保持不变,并且是新更新的值。所以这样我就无法跟踪修改或更改。

这是我的ApplicationContext文件,我在其中覆盖了保存调用。

 public class ApplicationContext : DbContext
{
    public ApplicationContext(DbContextOptions options) : base(options: options) { }

    public DbSet<Item> Item { get; set; }
    public DbSet<ChangeLog> ChangeLog { get; set; }        

    public override int SaveChanges()
    {
        var modifiedEntities = ChangeTracker.Entries();

        foreach (var change in modifiedEntities)
        {
            var entityType = change.Entity.GetType().Name;
            if (entityType == "LogItem")
                continue;

            if (change.State == EntityState.Modified)
            {
                foreach (var prop in change.OriginalValues.Properties)
                {
                    var id = change.CurrentValues["Id"].ToString();

                    //here both originalValue and currentValue  are same and it's newly updated value 
                    var originalValue = change.OriginalValues[prop]?.ToString();
                    var currentValue = change.CurrentValues[prop]?.ToString();
                    if (originalValue != currentValue)
                    {
                        ChangeLog.Add(
                            new ChangeLog()
                            {
                                CreationDateTime = DateTime.Now,
                                CreationUserId = 1,
                                Log = $"Edited item named {prop.Name} in {entityType} Id {id}.",
                                OldValue = originalValue,
                                NewValue = currentValue,
                                TableName = entityType,
                                FieldName = prop.Name
                            }
                        );
                    }
                }
            }
        }
        return base.SaveChanges();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的基础存储库。

public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IFullAuditedEntity, new()
{
    private readonly ApplicationContext context;

    public EntityBaseRepository(ApplicationContext context)
    {
        this.context = context;
    }

    public virtual T GetSingle(int id) => context.Set<T>().AsNoTracking().FirstOrDefault(x => x.Id == id);

    public virtual T Add(T entity) => Operations(entity: entity, state: EntityState.Added);

    public virtual T Update(T entity) => Operations(entity: entity, state: EntityState.Modified);

    public virtual T Delete(T entity) => Operations(entity: entity, state: EntityState.Deleted);

    public virtual T Operations(T entity, EntityState state)
    {
        EntityEntry dbEntityEntry = context.Entry<T>(entity);

        if (state == EntityState.Added)
        {
            entity.CreationDateTime = DateTime.UtcNow;
            entity.CreationUserId = 1;

            context.Set<T>().Add(entity);
            dbEntityEntry.State = EntityState.Added;
        }
        else if (state == EntityState.Modified)
        {
            entity.LastModificationDateTime = DateTime.UtcNow;
            entity.LastModificationUserId = 1;

            //var local = context.Set<T>().Local.FirstOrDefault(entry => entry.Id.Equals(entity.Id));
            //if (local != null)
            //{
            //    context.Entry(local).State = EntityState.Detached;
            //}

            dbEntityEntry.State = EntityState.Modified;
        }
        else if (state == EntityState.Deleted)
        {
            entity.DeletionFlag = true;
            entity.DeletionUserId = 1;
            entity.DeletionDateTime = DateTime.UtcNow;

            dbEntityEntry.State = EntityState.Modified;
        }

        return entity;
    }

    public virtual void Commit() => context.SaveChanges();

}
Run Code Online (Sandbox Code Playgroud)

最后是我的控制器,带有放置端点。

[Produces("application/json")]
[Route("api/Item")]
public class ItemController : Controller
{
    private readonly IItemRepository repository;
    private readonly IChangeLogRepository changeLogRepository;
    private readonly IMapper mapper;

    public ItemController(IItemRepository repository, IChangeLogRepository _changeLogRepository, IMapper mapper)
    {
        this.repository = repository;
        this.changeLogRepository = _changeLogRepository;
        this.mapper = mapper;
    }

    [HttpPut]
    public IActionResult Put([FromBody]ItemDto transactionItemDto)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (transactionItemDto.Id <= 0)
        {
            return new NotFoundResult();
        }

        Item item = repository.GetSingle(transactionItemDto.Id); //find entity first

        if (item == null)
        {
            return new NotFoundResult();
        }

        //map all the properties and commit
        var entity = mapper.Map<Item>(transactionItemDto);
        var updatedItem = repository.Update(entity);
        repository.Commit();

        return new OkObjectResult(mapper.Map<Item, ItemDto>(source: updatedItem));
    }
}
Run Code Online (Sandbox Code Playgroud)

我不确定我在哪里犯了任何错误,我试图在SO中检查这个案例,但没有运气。任何帮助将不胜感激,谢谢。

G_P*_*G_P 1

我想我看到了您的代码的问题。在你的控制器中:

    //map all the properties and commit
    var entity = mapper.Map<Item>(transactionItemDto);
    var updatedItem = repository.Update(entity);
    repository.Commit();
Run Code Online (Sandbox Code Playgroud)

在该代码中,您将获取 DTO 并将其映射到 Item 的新实例。Item 的新实例对当前数据库值一无所知,这就是为什么您会看到 OriginalValue 和 CurrentValue 相同的新值。

如果您重用此行中获得的 Item 项目变量:

Item item = repository.GetSingle(transactionItemDto.Id); //find entity first
Run Code Online (Sandbox Code Playgroud)

请注意,您需要获取启用跟踪的实体,这与存储库 GetSingle 使用 AsNoTracking 的方式不同。如果您使用该项目(现在具有原始/当前数据库值)并将 transactionItemDto 属性映射到它,如下所示:

var entityToUpdate = mapper.Map<ItemDto, Item>(transactionItemDto);
Run Code Online (Sandbox Code Playgroud)

然后,当您调用repository.Update 方法并将其传递给entityToUpdate 时,我相信您会看到正确的之前/之后值。

。。。。

我最初发布的旧(错误)答案:在您的 ApplicationContext 代码中,您有以下循环

foreach (var prop in change.OriginalValues.Properties)
Run Code Online (Sandbox Code Playgroud)

我相信这就是导致您的原始值/当前值相同的原因,因为您正在循环原始值属性。尝试将该循环更改为:

foreach (var prop in change.Properties)
Run Code Online (Sandbox Code Playgroud)

然后,尝试通过 prop 变量读取每个属性的值,如下所示:

var currentValue = prop.CurrentValue;
var originalValue = prop.OriginalValue;
Run Code Online (Sandbox Code Playgroud)

编辑:啊 - 我现在看到在你的代码中你正在尝试从change.OriginalValues集合中读取原始值,所以我认为这不会有帮助。