首页 > 解决方案 > 比较更改时,实体框架未获取原始实体

问题描述

我正在研究历史模型,在该模型中我获取一个实体,对其进行更改,然后再次获取与原始实体相同的实体并将其与记录更改进行比较。但我最终得到了 0 个更改,经过一番调查,我发现 Entity Framework 没有得到原始实体。它给了我同样的一个。这是我的代码。在这里我进行更改

public async Task<bool> AssignUserAsync(string userId, int ticketId, string changesAuthor)
        {
            
            var ticket = await GetByIdAsync(ticketId);
            ticket.AssignedUserId = userId;
            ticket.Status = Status.Assigned;
            var result = await Update(ticket, changesAuthor);
            return result;
        }

这里我调用了Update History方法

 public async Task<bool> Update(Ticket ticket, string user)
            {
            if (ticket == null)
                //throw exception
                return false;

            var ticketChanges = GetTicketChanges(await GetByIdAsync(ticket.Id), ticket, user);
            
            await UpdateHistory(ticketChanges);
            ticket.DateUpdated = DateTimeOffset.Now;

            var result = await _ticketRepository.UpdateAsync(ticket);
            return result;
        }

这是我的 GetById

public async Task<Ticket> GetByIdAsync(int id)
    { 
        return await _db.Tickets
            .Include(p => p.Project)
            .Include(t => t.Comments)
            .Include(p=>p.AssignedUser)
            .FirstOrDefaultAsync(p => p.Id == id);
    }

这里 Original 和要比较的票是同一个。我检查了数据库,但 AssignedUserId 不同。

  public List<History> GetTicketChanges(Ticket originalTicket, Ticket ticketToCompare, string changesAuthor)
        {
            List<History> ticketChanges = new();

            if (originalTicket == null || ticketToCompare == null)
                //throw exception
                return ticketChanges;

            var history = new History 
            { 
                DateCreated = DateTimeOffset.Now, 
                User = changesAuthor, 
                TicketId = ticketToCompare.Id 
            };

我删除了比较两个实体的长部分。我做错了什么,或者我怎么能得到原件?

标签: entity-framework

解决方案


此代码不会像您预期的那样工作,因为默认情况下 DbContext 正在跟踪您正在更新的初始实例,因此告诉 DbContext 再次检索该实例将返回您刚刚更新的相同引用。

例如:

using (var context = new AppDbContext())
{
    var ticket1 = context.Tickets.Single(ticketId);
    var ticket2 = context.Tickets.Single(ticketId);
    Assert.AreSame(ticket1, ticket2); // NUnit ReferenceEquals
}

这将断言为 true,因为 DbContext 将返回相同的引用。编辑ticket1 将导致在ticket2 上看到更改。

或者:

using (var context = new AppDbContext())
{
    var ticket1 = context.Tickets.AsNoTracking().Single(ticketId);
    var ticket2 = context.Tickets.AsNoTracking().Single(ticketId);
    Assert.AreSame(ticket1, ticket2); // NUnit ReferenceEquals
}

此断言将失败,因为不会跟踪返回的实例,因此您将收到不同的实例。

通过重写 DbContextOnSaveChanges方法并使用 ChangeTracker 检查可能已更新、添加或删除的感兴趣实体,然后检查其内容以构建审计记录,可以相当轻松地完成跟踪更改之类的操作。这样可以省去再次加载实体进行比较的麻烦。但是,这种方法的警告是,仅在每个实体的基础上记录审计更改才实用,而不是跨实体的相关图表。这意味着票证的更改跟踪器将允许您记录对票证所做的更改的增量,但不能记录票证的子引用。(这些将在更改跟踪器中单独跟踪)

您正在使用的结构使您的问题更加复杂,试图将所有内容隔离到重用的方法中。我曾尝试过可以使用哪些最小的更改来使其正常工作,但老实说,它最终变得更加纠结,因为您必须开始分离和重新附加参考。通常,围绕重新加载对同一实体的多个单独引用进行设计并不是一个好主意。使用分离的实体可以解决这个问题,但我通常不推荐这种方法,因为它会导致尝试附加和分离整个对象图的复杂性。(实体及其相关参考)我建议从 DbContext 进行更改跟踪,并允许它从会话状态解析当前用户,


推荐阅读