避免错误:实体对象不能被IEntityChangeTracker的多个实例引用

本文关键字:IEntityChangeTracker 引用 实例 不能 错误 实体 对象 | 更新日期: 2023-09-27 18:00:20

嗨,我使用的是实体框架5。我有三个模型:请求,区域,标签。CCD_ 1和CCD_。我按照这个创建了一个类似向导的表单。所以我有三个视图模型。一个视图表单获得基本信息,另外两个表单形成与请求相关的标签(多选)和区域(多选)。

为了将相同的请求会话传递给相应的操作,我使用此功能创建任何特定的请求模型实例并将其传递给相应操作

private Request GetRequest()
    {
        if (Session["request"] == null)
        {
            Debug.WriteLine("New Session Creation");
            Session["request"] = new Request();
        }
        Debug.WriteLine("SameSession");
        return (Request)Session["request"];
    }

我的控制器中只有一个Entities cxt = new Entities();实例我从表单中获取所选区域和标签的ID,并查询数据库,以便将它们添加到请求对象实例中,如图所示

区域行动结果

long val;
                foreach (var item in data.Regions)
                {
                    val = Convert.ToInt64(item);
                   request.Regions.Add(cxt.Regions.Single(r => r.Id == val));
                }

TagActionResult

long val;
                foreach (var item in data.Tags)
                {
                    val = Convert.ToInt64(item);
                    request.Tags.Add(cxt.Tags.Single(r => r.Id == val));
                }`enter code here`

然后我把请求保存在这里,就像一样

最终行动结果

Request_Log request = GetRequest();
            cxt.Request.Add(request); 
            cxt.SaveChanges(); 

调用保存请求的最后一个操作会引发错误An entity object cannot be referenced by multiple instances of IEntityChangeTracker.。我对实体框架很陌生,所以我不知道问题出在哪里。我只有上下文变量cxt的一个实例,它用于提取和添加区域和标记到请求对象,然后用同一上下文保存,所以我对如何在这里拥有多个上下文感到困惑。我希望我的问题有道理。

避免错误:实体对象不能被IEntityChangeTracker的多个实例引用

如果在控制器中使用Entities cxt = new Entities()创建上下文,则会为每个web请求创建一个新的ctx实例,尤其是对于选择区域和选择标记的两个向导表单,您有不同的上下文实例。因此,cxt.Regions.Single(...)cxt.Tags.Single(...)检索具有两个不同上下文的Request and Regions0和Tag实体,并将这些实体添加到request对象图中。当您稍后使用cxt.Request.Add(request)添加此request对象时(可能甚至在第三个ctx实例中),EF注意到相关的RegionTag引用了不同的上下文(通常情况下,由于某些virtual属性,它们是代理对象),这是禁止的,也是异常的根源。

如果在加载区域和标记之前禁用代理创建,然后在保存之前将实体重新附加到最终上下文,则可能会解决此问题。然而,在我看来,这是一个非常棘手的解决方案。我更喜欢在Session对象中保留除实体之外的任何其他内容(例如,一些简单的数据对象或视图模型,它们只表示从向导表单返回的数据),以避免出现此类问题,然后仅在保存到数据库的最后操作中将这些数据转换为实体。

例如,将所需的数据存储在三个会话值中——每个向导窗体一个:

Session["request"] = data.SomeRequestData; // your basic request info
Session["regions"] = data.Regions; // your collection of region IDs
Session["tags"] = data.Tags; // your collection of tag IDs

只有在最后一个操作中,你才能将各个部分组合成一个实体,并在一个上下文实例中完成:

var request = new Request();
var requestData = Session["request"] as SomeRequestDataModel;
request.Property1 = requestData.Property1;
request.Property2 = requestData.Property2;
// etc., just copy the properties
long val;
var regionIDs = Session["regions"] as IEnumerable<string>;
foreach (var item in regionIDs)
{
    val = Convert.ToInt64(item);
    request.Regions.Add(cxt.Regions.Single(r => r.Id == val)); // or .Find(val)
}
var tagIDs = Session["tags"] as IEnumerable<string>;
foreach (var item in tagIDs)
{
    val = Convert.ToInt64(item);
    request.Regions.Add(cxt.Tags.Single(r => r.Id == val)); // or .Find(val)
}
cxt.Request.Add(request);
cxt.SaveChanges();

(为了简单起见,我省略了对Request and Tags1的null的检查。)通过此过程,您可以确保作为最终对象图一部分的所有实体都附加到同一上下文实例ctx,并且不会发生异常。

相关文章:
  • 没有找到相关文章