EF核心变更跟踪-原始值和变更值存在问题 [英] EF Core change tracking - issue with original values and altered values

查看:77
本文介绍了EF核心变更跟踪-原始值和变更值存在问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有配置有.net core 2.0和EF core 2.0的Net core API.它包含存储库模式架构.

I have Net core API configured with .net core 2.0 and EF core 2.0. it contains repository pattern architecture.

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

Now, I am trying to implement Audit log for each save change using EF change tracker.

我的问题:每当我尝试为编辑/修改端点添加日志时,原始值和当前值都保持不变,并且是新近更新的值.因此,我无法跟踪修改或更改.

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

Here is my ApplicationContext file where I have overridden save call.

 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();
    }
}

这是我的基本存储库.

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();

}

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

And Lastly my controller with end point for put.

[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));
    }
}

我不确定我在哪里做错什么,我尝试在SO中检查这种情况,但是没有运气.任何帮助将不胜感激,谢谢.

I am not sure where I am doing any mistake, I tried to check this case in SO, but no luck. any help will be appreciated, thanks.

推荐答案

我认为您的代码存在问题.在您的控制器中:

I think I see the issue with your code. In your controller:

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

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

In that code you are taking your DTO and mapping it to a new instance of Item. That new instance of Item knows nothing of the current database values, that is why you are seeing the same new values for both OriginalValue and CurrentValue.

如果您重复使用在此行中获得的Item item变量:

If you reuse the Item item variable that you get in this line:

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

注意,但是,您需要获取具有跟踪功能的实体,而不是存储库GetSingle如何使用AsNoTracking进行跟踪.如果您使用该项目(现在具有原始/当前数据库值),并像下面这样将您的transactionItemDto属性映射到该项目上:

Note, you'll need to get the entity with tracking on however, vs how your repository GetSingle does it with AsNoTracking. If you use that item (which now has the original/current database values) and map your transactionItemDto properties onto it like this:

var entityToUpdate = mapper.Map<ItemDto, Item>(transactionItemDto);

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

Then, when you call your repository.Update method passing it entityToUpdate, I believe you'll see the correct before/after values.

...

旧(错误)答案:在您的ApplicationContext代码中,您具有以下循环

Old (wrong) answer I originally posted: In your ApplicationContext code you have the following loop

foreach (var prop in change.OriginalValues.Properties)

我认为这是导致您的原始值/当前值相同的原因,因为您正在遍历原始值属性.尝试将循环更改为:

I believe that is what is causing your original value/current values to be the same because you are looping over the original values properties. Try changing that loop to:

foreach (var prop in change.Properties)

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

Then, try reading the values off each property via the prop variable like so:

var currentValue = prop.CurrentValue;
var originalValue = prop.OriginalValue;

嗯-我现在看到在您的代码中,您正在尝试从change.OriginalValues集合中读取原始值,因此我认为这不会有所帮助.

Ah - I see now that in your code you are trying to read the original value from the change.OriginalValues collection, so I don't think this is going to help.

这篇关于EF核心变更跟踪-原始值和变更值存在问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆