C# 使用实体框架核心更新相关数据

C# 使用实体框架核心更新相关数据,c#,asp.net-web-api,entity-framework-core,asp.net-core-webapi,C#,Asp.net Web Api,Entity Framework Core,Asp.net Core Webapi,我正在用实体框架核心构建一个简单的webapi。我使用模型和视图模型来管理客户机实际接收的数据。以下是我创建的模型和视图模型: public class Team : BaseEntity { [Key] public int TeamId { get; set; } [Required] public string TeamName { get; set; } public List<TeamAgent> TeamAgents { get;

我正在用实体框架核心构建一个简单的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; }
}

但这显然不是很好的方法。用实体框架核心更新相关项目的正确方法是什么?

Julie Lerman在2016年4月的文章中阐述了这一点。如果你还没有看过这篇文章,那就值得一读。遗憾的是,到了EFCore2.0,仍然没有内置的方法来更新对象图

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

在我最近的项目中,我采取了一种稍微不同的方法,主要是因为到目前为止只有一个操作需要更新子集合。在我不得不再次这样做的时候,我可能会牢记朱莉的建议并进行重构

基本上,它只是一个手动对象图更新,看起来与EF6.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();
}
//
公共覆盖无效更新(组实体){
//数据库中当前存在的实体
var group=DbContext.Groups.Include(c=>c.Contacts)
.FirstOrDefault(g=>g.Id==entity.Id);
//更新父对象上的属性
DbContext.Entry(组).CurrentValues.SetValues(实体);
//删除或更新子集合项
var groupContacts=group.Contacts.ToList();
foreach(groupContacts中的var groupContact){
var contact=entity.Contacts.SingleOrDefault(i=>i.ContactId==groupContact.ContactId);
如果(联系人!=null)
DbContext.Entry(groupContact).CurrentValues.SetValues(contact);
其他的
DbContext.Remove(groupContact);
}
//添加新项目
foreach(entity.Contacts中的var contact){
if(groupContacts.All(i=>i.Id!=contact.Id)){
group.Contacts.Add(联系人);
}
}
DbContext.SaveChanges();
}

这要归功于@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();
    }
}
资料来源:


EF核心部分仍然为空。这意味着没有好的(正确的)通用方法来做这件事。@IvanStoev,请添加您的评论作为答案,我会接受。谢谢,但这绝对不是我想要的答案。希望在某个时候,MS或第三方软件包(如GraphDiff for EF6)能够解决这些问题。我已经为这个问题创建了一个通用解决方案。请看一下我的答案,非常感谢您为我这几天遇到的一个问题提供了这个简单的解决方案,这个问题与为自引用表保存数据有关。
/// <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();
}
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();
    }
}