实体框架,引用完整性约束违规发生错误更新实体从断开区域 [英] Entity framework, referential integrity constraint violation occurred error on updating entity from disconnected area

查看:122
本文介绍了实体框架,引用完整性约束违规发生错误更新实体从断开区域的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下模型

  public class Order 
{
[Key]
public virtual string OrderNo {get; set;}
public virtual IList< OrderItem>项目{get; set;}
}

public class OrderItem
{
[Key]
public virtual string ItemNo {get; set;}
public virtual string ParentItemNo {get; set;}
public virtual string OrderNo {get; set;}

public virtual OrderItem ParentItem {get; set;}
public virtual IList< OrderItem> ChildItems {get; set;}
public virtual IList< ItemProperty> ItemProperties {get; set;}
public virtual Order Order {get; set;}
}

public class ItemProperty
{
[Key]
public virtual string PropertyNo {get; set;}
public virtual string ParentPropertyNo {get; set;}
public virtual string OrderItemNo {get; set;}

public virtual ItemProperty ParentProperty {get; set;}
public virtual IList< ItemProperty> ChildProperties {get; set;}
public virtual OrderItem OrderItem {get; set;}
}

我正在断开连接的区域运行(与我们从实体框架上下文断开的服务)


  1. 我创建一个订单保存到数据库

客户端:

  service.CreateOrder(new Order(){OrderN =fksjdf1}); 

服务器:

 code> using(EfDbContext context = new EfDbContext())
{
context.Orders.Add(order);
context.SaveChanges();
}




  1. 我需要添加一个或多个OrderItems以前添加的订单

客户端:

  var order = service.GetOrder(fksjdf1); 

var item1 = new OrderItem();
item1.ItemNo =i1;
item1.Order = order;
item1.OrderNo = order.OrderNo;
item1.ItemProperties.Add(new ItemProperty()
PropertyNo =p1,
OrderItem = item1
})
order.Items.Add(item1);

var item2 = new OrderItem();
item2.ItemNo =i2;
item2.Order = order;
item2.OrderNo = order.OrderNo;
item2.ItemProperties.Add(new ItemProperty()
PropertyNo =p2,
OrderItem = item2
});
item2.ItemProperties.Add(new ItemProperty()
PropertyNo =p3,
OrderItem = item2
})
order.Items.Add(item2);
service.UpdateOrder(order);

服务器:

 code> using(EfDbContext context = new EfDbContext())
{
DbEntityEntry dbEntityEntry = context.Entry(order);
if(dbEntityEntry.State == EntityState.Detached)
{
// ERROR
context.Set< Order>()。
}

dbEntityEntry.State = EntityState.Modified;
context.SaveChanges();
}

错误:发生参照完整性约束违规:定义引用的属性值约束在关系中的主体和从属对象之间不一致。



为什么我看到这个错误?为什么我不更新此实体?



如何使用实体框架从断开区域?



编辑1:

  public Order GetOrder(string orderNo)
{
using(EfDbContext context = new EfDbContext())
{
context.Configuration.ProxyCreationEnabled = false;
var order = context.Orders
.Include(o => o.OrderItems
.Select(z => z.ItemProperties
.Select(y => y .ChildProperties)))
.Where(o => o.OrderNo == orderNo)
.FirstOrDefault();
}
}


解决方案

你可能会出现此错误,因为您不在 ItemProperty 中设置 OrderItemNo ,如下所示:

  var item1 = new OrderItem(); 
item1.ItemNo =i1;
item1.Order = order;
item1.OrderNo = order.OrderNo;
item1.ItemProperties.Add(new ItemProperty {
PropertyNo =p1,
OrderItem = item1,
OrderItemNo = item1.ItemNo // =i1
});
order.Items.Add(item1);

//和item2相同

当订单被附加到上下文,相关订单项和项目属性也被附加。导航属性 OrderItem ItemProperty 是指具有您提供的密钥的实体(i1 ),但外键 OrderItemNo 没有此键值。这是异常抱怨的不一致。



修改



不幸的是不容易更新数据库中的分离对象图。将实体的状态设置为修改仅将此实体标记为修改而不关联实体。



您的 GetOrder 方法使事情变得复杂,因为您正在热切地将已有的项目和更多相关的东西加载到客户端,然后添加新的 OrderItem s,并将此新修改的对象图发送回服务器。在服务器端,您现在有这样的问题:您必须处理已经存在的项目(不要将它们插入到数据库中)而不是新项目(将它们插入到数据库中)。为此,您需要检测哪些项目是旧的,哪些是新的。如果您不在实体本身传输一些指示实体是否为新的标志,则必须再次查询数据库,并将从DB加载的对象图与从客户端发送的分离对象图进行比较。一般草图如何做: https://stackoverflow.com/a/5540956/270591 (这是只有一个父母有孩子收藏,如果涉及到孙子藏书 - 像在你的模型 - 它变得越来越复杂。)



在我看来,你可以简化整个程序如果您不仅有这个全局的 service.UpdateOrder(order)方法,它尝试处理对象图中的所有可能的更改,还有一些利用您拥有的知识的专门方法关于图的更改 - 在这种情况下,是一种用于向现有订单添加新的 OrderItem s的专门方法。它可能如下所示:

  var order = service.GetOrder(fksjdf1); 

var newOrderItems = new List< OrderItem>();

var item1 = new OrderItem();
item1.ItemNo =i1;
item1.OrderNo = order.OrderNo; //离开Order属性null
item1.ItemProperties.Add(new ItemProperty {PropertyNo =p1});
newOrderItems.Add(item1);

// item2
newOrderItems.Add(item2);

service.AddNewOrderItems(newOrderItems);

服务方式是:



<$使用(EfDbContext context = new EfDbContext())
{
foreach() var newOrderItem in newOrderItems)
context.Set< OrderItem>()。Add(newOrderItem);

context.SaveChanges();
}
}


I have below model

public class Order
{
    [Key]
    public virtual string OrderNo {get;set;}
    public virtual IList<OrderItem> Items {get;set;}
}

public class OrderItem
{
    [Key]
    public virtual string ItemNo {get; set;}
    public virtual string ParentItemNo {get;set;}
    public virtual string OrderNo {get;set;}

    public virtual OrderItem ParentItem {get;set;}
    public virtual IList<OrderItem> ChildItems {get;set;}
    public virtual IList<ItemProperty> ItemProperties {get;set;}
    public virtual Order Order {get;set;}
}

public class ItemProperty
{
    [Key]
    public virtual string PropertyNo {get; set;}
    public virtual string ParentPropertyNo {get;set;}
    public virtual string OrderItemNo {get;set;}

    public virtual ItemProperty ParentProperty {get;set;}
    public virtual IList<ItemProperty> ChildProperties {get;set;}
    public virtual OrderItem OrderItem {get;set;}
}

I'm running on disconnected area (with service our disconnected from the Entity Framework context)

  1. I create a order and save to database

client:

service.CreateOrder(new Order() { OrderN="fksjdf1" });

server:

using(EfDbContext context = new EfDbContext())
{
    context.Orders.Add(order);
    context.SaveChanges();
}

  1. I need to add one or more OrderItems to previously added order

client:

var order = service.GetOrder("fksjdf1");

var item1 = new OrderItem();
item1.ItemNo="i1";
item1.Order=order;
item1.OrderNo=order.OrderNo;
item1.ItemProperties.Add(new ItemProperty()
    PropertyNo="p1",
    OrderItem = item1
})
order.Items.Add(item1);

var item2 = new OrderItem();
item2.ItemNo="i2";
item2.Order=order;
item2.OrderNo=order.OrderNo;
item2.ItemProperties.Add(new ItemProperty()
    PropertyNo="p2",
    OrderItem = item2
});
item2.ItemProperties.Add(new ItemProperty()
    PropertyNo="p3",
    OrderItem = item2
})
order.Items.Add(item2);
service.UpdateOrder(order);

server:

using(EfDbContext context = new EfDbContext())
{
    DbEntityEntry dbEntityEntry = context.Entry(order);
    if (dbEntityEntry.State == EntityState.Detached)
    {
        // ERROR
        context.Set<Order>().Attach(order);
    }

    dbEntityEntry.State = EntityState.Modified;
    context.SaveChanges();
}

Error: A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.

Why I see this error? Why I not update this entity?

How can i use entity framework from disconnected area?

EDIT 1:

public Order GetOrder(string orderNo)
{
using (EfDbContext context = new EfDbContext())
            {
                context.Configuration.ProxyCreationEnabled = false;
                var order = context.Orders
                    .Include(o => o.OrderItems
                                      .Select(z => z.ItemProperties
                                                       .Select(y => y.ChildProperties)))
                                                       .Where(o => o.OrderNo == orderNo)
                    .FirstOrDefault();
            }
}

解决方案

You probably have this error because you don't set OrderItemNo in ItemProperty, like so:

var item1 = new OrderItem();
item1.ItemNo="i1";
item1.Order=order;
item1.OrderNo=order.OrderNo;
item1.ItemProperties.Add(new ItemProperty {
    PropertyNo="p1",
    OrderItem = item1,
    OrderItemNo = item1.ItemNo // = "i1"
});
order.Items.Add(item1);

// and the same for item2

When the order gets attached to the context, related order items and item properties get attached as well. The navigation property OrderItem of ItemProperty refers to an entity with a key you supplied ("i1"), but the foreign key OrderItemNo doesn't have this key value. That's the inconsistency the exception is complaining about.

Edit

Unfortunately it is not that easy to update a detached object graph in the database. Setting the state of an entity to Modified only marks this entity as Modified and no related entity.

Your GetOrder method makes things complicated because you are eagerly loading already existing items and more related stuff to the client, then add new OrderItems on client side and send this new modified object graph back to the server. On server side you now have the problem that you must handle the already existing items differently (don't insert them into the database) than the new items (insert them into the database). To do this, you need to detect which items are old and which are new. If you don't transport some flag in the entities themselves that would indicate if the entity is new or not, you must query the database again and compare the object graph loaded from the DB with the detached object graph sent from the client. A general sketch how it can be done is here: https://stackoverflow.com/a/5540956/270591 (That's only for a parent with child collection. If grandchild collections are involved - like in your model - it is getting more complex.)

In my opinion you can simplify the whole procedure if you would not only have this global service.UpdateOrder(order) method that tries to handle all possible changes in the object graph but also some specialized methods that leverage the knowledge you have about the change of the graph - in this case a specialized method for adding new OrderItems to an existing order. It could look like this:

var order = service.GetOrder("fksjdf1");

var newOrderItems = new List<OrderItem>();

var item1 = new OrderItem();
item1.ItemNo="i1";
item1.OrderNo=order.OrderNo; // leave the Order property null
item1.ItemProperties.Add(new ItemProperty { PropertyNo="p1" });
newOrderItems.Add(item1);

// the same for item2
newOrderItems.Add(item2);

service.AddNewOrderItems(newOrderItems);

And the service method would be:

public void AddNewOrderItems(List<OrderItem> newOrderItems)
{
    using(EfDbContext context = new EfDbContext())
    {
        foreach (var newOrderItem in newOrderItems)
            context.Set<OrderItem>().Add(newOrderItem);

        context.SaveChanges();
    }
}

这篇关于实体框架,引用完整性约束违规发生错误更新实体从断开区域的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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