实体框架:对多对多关系的参照完整性约束违规

时间:2012-04-03 20:51:26

标签: c# entity-framework

嘿,我有一个带有一堆inproc缓存和实体框架的应用程序。当我想写一个实体的更新时,我重新附加了缓存的副本。我在上下文的生命周期中跟踪我所附加的所有内容,因此我不会尝试将它们附加两次。

我在附加时发生错误(很少在大多数情况下这种方法很好并且非常快),其中包含以下内容:

  

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

我仔细看了一下看似正常的实体。我认为这个问题是由于修复程序运行时附加/分离外键。

是否有一个很好的方法可以获得有关此错误的更多信息,或者是否因为实体处于EF不期望的状态而发生?

编辑: DB Diagram(注意我正在使用codefirst我只是使用EDMX工具来制作图表,为了简单起见,我还从模型中删除了一堆常规属性)

enter image description here

7 个答案:

答案 0 :(得分:46)

除了多对多关系之外,您在模型中显然拥有的PersonLocation之间的一对多关系可能会发生错误。例如,以下代码将抛出异常:

using (var context = new MyContext())
{
    var person = new Person
    {
        CurrentLocationId = 1,
        CurrentLocation = new Location { Id = 2 }
    };
    context.People.Attach(person); // Exception
}

“定义引用约束的属性值”是外键属性值CurrentLocationId和主键值CurrentLocation.Id。如果这些值不同,则抛出异常。 (虽然允许CurrentLocationnull。)

在我看来,只能为外键关联抛出此异常,因为只有这种类型的关联,您才具有在模型中定义参照约束的属性。它不能被独立的协会抛出。由于每个多对多关系都是一个独立的关联(模型中没有外键属性),我的猜测是错误与你的多对多关系无关,而是与一对多关系。

答案 1 :(得分:6)

我遇到了一个非常相似的例外:

"A referential integrity constraint violation occurred: 
The property value(s) of 'ObjectA.PropertyX' on one end of a relationship 
do not match the property value(s) of 'ObjectB.PropertyY' on the other end."

原因是这样的: Web API的客户端发送了包含导航属性的整个对象的PUT请求(在此示例中,ObjectA(更准确地说,ObjectB.ObjectA)是导航属性,并且由客户端完全提供)。发生这种情况是因为客户端从服务器接收整个对象并将其原样弹回到服务器并进行微小更改。

另一方面,ObjectB.PropertyY刚刚被更改(这首先是PUT请求的原因)。

由于ObjectB.PropertyY是对同一对象ObjectA(外键)的引用,因此EF尝试协调此对象并因上述异常而失败。

解决方案很简单:

ObjectB.ObjectA = null;

之前SaveChanges()完全解决了这个问题。

我希望这有助于某人。

答案 2 :(得分:2)

我刚刚遇到了同样的问题,而我的解决方案是我已经为协会添加了映射,然后设置了参考约束。

为了解决这个问题,我必须打开关联的映射窗口,并且有一个删除映射的链接。完成映射详细信息窗口后,表示不允许使用映射。看起来添加引用约束会使任何映射都保留。

如果将来其他人正在寻找此错误消息的解决方案,可能值得发帖。

答案 3 :(得分:1)

@LukeMcGregor嗨,

我认为我可以提供一个不同的视角作为有同样问题的人。

在我完成所有必要的检查后,我可以说我更喜欢这个错误。

因为在我的场景中:我想要包含一个导致不匹配错误的对象。它是您的方案中的位置对象。如果我添加一个带有ID的对象,我会收到此错误,因为上一个对象中的ID(未更新的ID)与更新的ID不匹配。

但这不是一个大问题。作为解决方案;如果它仍在UI端,则如果该对象仍然存在,则可能仍然包含该对象。

当您收到用户的更新请求时,您将清空对象。 (= Null)或者您将在服务端更新(附加,修改......等)之前使用用户更新的ID更新对象,并以这种方式更新它。

那就是它。它可以保留在数据库和图表中。

答案 4 :(得分:1)

要添加到@Slauma的答案中,不仅是在向上下文中添加对象时。例如,如果您在Person中编辑CurrentLocationId,则还需要编辑嵌入在Person对象中的CurrentLocation对象。 EF将自动填充CurrentLocation对象,因为CurrentLocationId在CurrentLocation的表中具有外键。当您编辑CurrentLocationId而不同时更新CurrentLocation对象时,它们将变得不同步。这就是在这种情况下导致异常的原因。

因此,假设您需要更新Person对象的CurrentLocationId。我们假设您已经预提取了Person数据和Location数据。

public class DbData 
{
    List<Person> PersonList;
    List<Location> LocationList;
    public DbData()
    {
        using (var context = new MyContext())
        {
             PersonList = context.Persons.ToList();
             LocationList = context.Locations.ToList();
        }
    }

    public void UpdatePersonLocation(Person person, int newLocationId)
    {
        using (var context = new MyContext())
        {
            var location = LocationList.Where(l=>l.id==newLocationId).Single();
            //you need to update both the id and the location for this to not throw the exception
            person.CurrentLocationId == newLocationId;
            person.CurrentLocation == location;  
            context.Entry(person).State = System.Data.Entity.EntityState.Modified;
            context.SaveChanges();
        }
    }
    //or if you're giving it the location object...
    public void UpdatePersonLocation(Person person, Location location)
    {
        using (var context = new MyContext())
        {
            //you need to update both the id and the location for this to not throw the exception
            person.CurrentLocationId == location.id;
            person.CurrentLocation == location;  
            context.Entry(person).State = System.Data.Entity.EntityState.Modified;
            context.SaveChanges();
        }
    }
}

答案 5 :(得分:0)

我刚遇到这个问题并提出了一个非常快速的解决方案。我的问题是有很多桌子。

Public class Pictures_Tag
{
    [Key]
    [Column(Order = 0)]
    [ForeignKey("Picture")]
    public Int16 Picture_ID { get; set; }
    [Key]
    [Column(Order = 1)]
    [ForeignKey("ImageTag")]
    public Int16 ImageTag_ID { get; set; }
    public virtual Picture Picture { get; set; }
    public virtual ImageTag ImageTag { get; set; }
}

我添加了我分配Picture = db.Pictures...的行,然后它工作正常(不完全确定原因)

[HttpPost]
public ActionResult Edit2(WebSiteEF2017C.Models.Pictures_Tag p)
{     
    using (var db = new thisModel(Session["org"].ToString())
    {
         p.Picture = db.Pictures.Where(z => z.ID == p.Picture_ID).FirstOrDefault();
         db.Pictures_Tags.Attach(p);
         db.Entry(p).State = EntityState.Modified;
         db.SaveChanges();
         return View(db.Pictures_Tags.Include(x => x.Picture)
                    .Where(n => n.Picture_ID == p.Picture_ID & n.ImageTag_ID == p.ImageTag_ID).FirstOrDefault());
    }
}

答案 6 :(得分:0)

尝试将任何子类对象设置为空 MainObject.SubObject1 = null; Person.Address=null; // 地址对象为 null 而不是 id