使用 Entity Framework Core 更新相关数据

Posted

技术标签:

【中文标题】使用 Entity Framework Core 更新相关数据【英文标题】:Updating related data with Entity Framework Core 【发布时间】:2017-08-01 19:13:05 【问题描述】:

我正在使用 Entity Framework Core 构建一个简单的 webapi。我正在使用模型和视图模型来管理客户端实际接收的数据。这是我创建的模型和视图模型:

public class Team : BaseEntity

    [Key]
    public int TeamId  get; set; 
    [Required]
    public string TeamName  get; set; 
    public List<TeamAgent> TeamAgents  get; set; 


public class TeamViewModel

    [Required]
    public int TeamId  get; set; 
    [Required]
    public string TeamName  get; set; 
    [DataType(DataType.Date)]
    public DateTime DateCreated  get; set; 
    [DataType(DataType.Date)]
    public DateTime DateModified  get; set; 
    public List<TeamAgent> TeamAgents  get; set; 


public class TeamAgent : BaseEntity

    [Key]
    public int TeamAgentId  get; set; 
    [ForeignKey("Agent")]
    public int AgentId  get; set; 
    [JsonIgnore]
    public virtual Agent Agent  get; set; 
    [ForeignKey("Team")]
    public int TeamId  get; set; 
    [JsonIgnore]
    public virtual Team Team  get; set; 
    [Required]
    public string Token  get; set; 


public class TeamAgentViewModel

    [Required]
    public virtual AgentViewModel Agent  get; set; 
    [Required]
    public string Token  get; set; 

现在为了更新,我在控制器中创建了一个更新方法:

[HttpPut("id")]
public async Task<IActionResult> Update(int id, [FromBody]TeamViewModel teamVM)

    if (ModelState.IsValid)
    
        var team = await _context.Teams
                            .Include(t => t.TeamAgents)
                            .SingleOrDefaultAsync(c => c.TeamId == id);

        team.TeamName = teamVM.TeamName;

        // HOW TO HANDLE IF SOME TEAMAGENTS GOT ADDED OR REMOVED???

        _context.Teams.Update(team);
        await _context.SaveChangesAsync();

        return new NoContentResult();
    
    return BadRequest(ModelState);

我陷入了如何更新连接到团队的 TeamAgents 的问题上。我尝试和工作的一件事是删除所有 TeamAgent,然后在每次更新团队数据时创建新的。方法如下:

team.TeamAgents.Clear();
await _context.SaveChangesAsync();
team.TeamAgents.AddRange(teamVM.TeamAgents);

_context.Teams.Update(team);
await _context.SaveChangesAsync();

但这显然不是很好的方法。使用 Entity Framework Core 更新相关项目的正确方法是什么?

【问题讨论】:

EF Core 的 Saving Data - Disconnected Entities 部分仍然为空。这意味着没有好的(正确的)通用方法可以做到这一点。 @IvanStoev,请添加您的评论作为答案,我会接受。 谢谢,但这绝对不是我想要回答的问题。希望在某些时候这种情况将由 MS 或 EF6 的 GraphDiff 等第 3 方包解决。 我已经为这个问题创建了一个通用解决方案。您可以在这里查看我的答案***.com/a/63919095/105445 【参考方案1】:

Julie Lerman 在 2016 年 4 月的文章 Handling the State of Disconnected Entities in EF 中谈到了这一点。如果您还没有看过这篇文章,那么值得一读。遗憾的是,从 EF Core 2.0 开始,仍然没有内置方法来更新对象图。

Julie 的文章中提到的方法基本上是通过向对象添加属性并将状态发送给客户端来跟踪分离实体的状态。客户端可以修改状态并将其发送回服务器,然后您当然可以使用该信息做正确的事情。

在我最近的项目中,我采用了一种略有不同的方法,主要是因为到目前为止只有一个操作需要更新子集合。在我必须再次执行此操作时,我可能会将 Julie 的建议牢记在心并进行重构。

基本上,它只是一个手动对象图更新,看起来与您在 EF 6.0 中采用的方法非常相似。需要注意的一点是,现在使用 EF Core,您只需调用 Remove() 即可传递实体,而不必关心它属于哪个 dbSet。

/// <param name="entity"></param>
public override void Update(Group entity) 
    // entity as it currently exists in the db
    var group = DbContext.Groups.Include(c => c.Contacts)
        .FirstOrDefault(g => g.Id == entity.Id);
    // update properties on the parent
    DbContext.Entry(group).CurrentValues.SetValues(entity);
    // remove or update child collection items
    var groupContacts = group.Contacts.ToList();
    foreach (var groupContact in groupContacts) 
        var contact = entity.Contacts.SingleOrDefault(i => i.ContactId == groupContact.ContactId);
        if (contact != null)
            DbContext.Entry(groupContact).CurrentValues.SetValues(contact);
        else
            DbContext.Remove(groupContact);
    
    // add the new items
    foreach (var contact in entity.Contacts) 
        if (groupContacts.All(i => i.Id != contact.Id)) 
            group.Contacts.Add(contact);
        
    
    DbContext.SaveChanges();

【讨论】:

非常感谢这个简单的解决方案解决了我这几天遇到的与为自引用表保存数据有关的问题。【参考方案2】:

感谢@Slauma。

public void Update(UpdateParentModel model)

    var existingParent = _dbContext.Parents
        .Where(p => p.Id == model.Id)
        .Include(p => p.Children)
        .SingleOrDefault();

    if (existingParent != null)
    
        // Update parent
        _dbContext.Entry(existingParent).CurrentValues.SetValues(model);

        // Delete children
        foreach (var existingChild in existingParent.Children.ToList())
        
            if (!model.Children.Any(c => c.Id == existingChild.Id))
                _dbContext.Children.Remove(existingChild);
        

        // Update and Insert children
        foreach (var childModel in model.Children)
        
            var existingChild = existingParent.Children
                .Where(c => c.Id == childModel.Id && c.Id != default(int))
                .SingleOrDefault();

            if (existingChild != null)
                // Update child
                _dbContext.Entry(existingChild).CurrentValues.SetValues(childModel);
            else
            
                // Insert child
                var newChild = new Child
                
                    Data = childModel.Data,
                    //...
                ;
                existingParent.Children.Add(newChild);
            
        

        _dbContext.SaveChanges();
    

来源:

https://***.com/a/27177623/3850405

【讨论】:

以上是关于使用 Entity Framework Core 更新相关数据的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework Core 性能优化

在 Entity Framework Core 中使用 [ComplexType]

在 Entity Framework Core 中使用 SQL 视图

使用 Entity Framework Core 更新相关数据

使用 Entity Framework Core 自动增加部分主键

如何使用 Entity Framework Core 模拟异步存储库