【问题标题】:Avoiding Error: An entity object cannot be referenced by multiple instances of IEntityChangeTracker避免错误:IEntityChangeTracker 的多个实例不能引用实体对象
【发布时间】:2014-04-27 17:28:40
【问题描述】:

您好,我正在使用实体框架 5。我有三个模型:请求、区域、标签。 Request and RegionsRequest and Tags 之间存在多对多关系。我跟着this 创建了一个类似向导的表单。所以我有三个视图模型。一个视图表单获取基本信息,另外两个表单获取与请求相关的标签(多选)和区域(多选)。

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

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 并查询数据库,以便像这样将它们添加到请求对象实例中。

RegionActionResult

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`

然后我把请求保存在这里

FinalActionResult

Request_Log request = GetRequest();

            cxt.Request.Add(request); 
            cxt.SaveChanges(); 

调用最终操作来保存请求会引发错误An entity object cannot be referenced by multiple instances of IEntityChangeTracker. 我对实体框架很陌生,所以我不知道问题是什么。我只有一个上下文变量cxt 的实例,它用于获取区域和标签并将其添加到请求对象中,然后使用相同的上下文保存,所以我不知道如何在这里拥有多个上下文。我希望我的问题有意义。

【问题讨论】:

    标签: c# entity-framework asp.net-mvc-4 session viewmodel


    【解决方案1】:

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

    如果您在加载区域和标签之前禁用代理创建,然后在保存之前将实体重新附加到最终上下文,则可能会解决该问题。但是,在我看来,这是一个非常hacky的解决方案。我非常希望在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();
    

    (为简单起见,我省略了对 nullSession["XXX"] 的检查。)通过此过程,您可以确保属于最终对象图的所有实体都附加到同一上下文实例 @987654339 @ 并且您的异常不会发生。

    【讨论】:

    • 很好的解释。真的很有帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-01
    • 2011-10-12
    • 1970-01-01
    相关资源
    最近更新 更多