Asp.net web api 首先使用ASP.NET Web API编写EF5代码:使用多对多关系更新实体

Asp.net web api 首先使用ASP.NET Web API编写EF5代码:使用多对多关系更新实体,asp.net-web-api,ef-code-first,many-to-many,entity-framework-5,Asp.net Web Api,Ef Code First,Many To Many,Entity Framework 5,我试图首先使用ASP.NET Web API和Entity Framework 5代码更新数据库中的客户,但它不起作用。我的实体如下所示: public class CustomerModel { public int Id { get; set; } public string Name { get; set; } // More fields public ICollection<CustomerTypeModel> CustomerTypes

我试图首先使用ASP.NET Web API和Entity Framework 5代码更新数据库中的
客户
,但它不起作用。我的实体如下所示:

public class CustomerModel
{
    public int Id { get; set; }
    public string Name { get; set; }

    // More fields

    public ICollection<CustomerTypeModel> CustomerTypes { get; set; }
}

public class CustomerTypeModel
{
    public int Id { get; set; }
    public string Type { get; set; }

    [JsonIgnore]
    public ICollection<CustomerModel> Customers { get; set; }
}
这会更新客户字段,但会忽略相关的客户类型。传入的
客户
对象确实包含一个
客户类型列表
,它应该与以下内容关联:

[0] => { Id: 1, Type: "Finance", Customers: Null },
[1] => { Id: 2, Type: "Insurance", Customers: Null }
[2] => { Id: 3, Type: "Electronics", Customers: Null }
但是EF没有查看此列表并添加/删除关联实体,而是忽略它。新关联将被忽略,而现有关联将保留,即使它们应该被删除

在将客户插入数据库时,我遇到了类似的问题,当我将这些实体的状态调整为
EntityState.Unchanged
时,问题得到了解决。当然,我尝试在我的更新场景中应用同样的神奇修复:

public void Put([FromBody]CustomerModel customer)
{
    using (var context = new MyContext())
    {
        foreach (var customertype in customer.CustomerTypes)
        {
            context.Entry(customertype).State = EntityState.Unchanged;
        }

        context.Customers.Attach(customer);
        context.Entry(customer).State = EntityState.Modified;
        context.SaveChanges();
    }
}
但EF始终显示相同的行为

有没有办法解决这个问题?或者我真的应该手动清除
客户类型列表,然后手动添加它们吗

提前谢谢


JP

仅通过设置实体状态并不能真正解决此问题。必须首先从数据库中加载客户,包括其所有当前类型,然后根据已发布客户的更新类型集合从加载的客户中删除类型或向其添加类型。更改跟踪将完成从联接表中删除条目或插入新条目的其余操作:

public void Put([FromBody]CustomerModel customer)
{
    using (var context = new MyContext())
    {
        var customerInDb = context.Customers.Include(c => c.CustomerTypes)
            .Single(c => c.Id == customer.Id);

        // Updates the Name property
        context.Entry(customerInDb).CurrentValues.SetValues(customer);

        // Remove types
        foreach (var typeInDb in customerInDb.CustomerTypes.ToList())
            if (!customer.CustomerTypes.Any(t => t.Id == typeInDb.Id))
                customerInDb.CustomerTypes.Remove(typeInDb);

        // Add new types
        foreach (var type in customer.CustomerTypes)
            if (!customerInDb.CustomerTypes.Any(t => t.Id == type.Id))
            {
                context.CustomerTypes.Attach(type);
                customerInDb.CustomerTypes.Add(type);
            }

        context.SaveChanges();
    }
}

更清洁的解决方案是:

public void Put([FromBody]CustomerModel customer)
{
    using (var context = new MyContext())
    {
        var customerInDb = context.Customers.Include(c => c.CustomerTypes)
            .Single(c => c.Id == customer.Id);

        // Updates the Name property
        context.Entry(customerInDb).CurrentValues.SetValues(customer);

        // Remove types
        customer.CustomerTypes.Clear();

        // Add new types
        foreach (var type in customer.CustomerTypes) 
        {
            context.CustomerTypes.Attach(type);
            customerInDb.CustomerTypes.Add(type);
        }

        context.SaveChanges();
    }
}

是的,我害怕这种手工的做事方式。谢谢你的代码,它工作起来很有魅力,还帮我省去了一些与状态抗争的痛苦:)有没有办法让这个通用,这样它就适用于任何导航属性?@Codemiester:你可以看看这里评论中提到的GraphDiff库:有没有办法让这个通用,这样它就适用于任何导航属性?
public void Put([FromBody]CustomerModel customer)
{
    using (var context = new MyContext())
    {
        var customerInDb = context.Customers.Include(c => c.CustomerTypes)
            .Single(c => c.Id == customer.Id);

        // Updates the Name property
        context.Entry(customerInDb).CurrentValues.SetValues(customer);

        // Remove types
        customer.CustomerTypes.Clear();

        // Add new types
        foreach (var type in customer.CustomerTypes) 
        {
            context.CustomerTypes.Attach(type);
            customerInDb.CustomerTypes.Add(type);
        }

        context.SaveChanges();
    }
}