C# ADO.Net Entity Framework 一个实体对象不能被多个 IEntityChangeTracker 实例引用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/694625/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 13:46:02  来源:igfitidea点击:

ADO.Net Entity Framework An entity object cannot be referenced by multiple instances of IEntityChangeTracker

c#entity-frameworkado.net

提问by Peter

I am trying to save my contact, which has references to ContactRelation (just the relationship of the contact, married, single, etc) and Country. But everytime I try to save my contact, which is validated I get the exception "ADO.Net Entity Framework An entity object cannot be referenced by multiple instances of IEntityChangeTracker"

我正在尝试保存我的联系人,其中包含对 ContactRelation(仅联系人、已婚、单身等的关系)和国家/地区的引用。但是每次我尝试保存已验证的联系人时,我都会收到异常“ADO.Net 实体框架实体对象不能被 IEntityChangeTracker 的多个实例引用”

public Contact CreateContact(Contact contact)
{
    _entities.AddToContact(contact); //throws the exception
    _entities.SaveChanges();
    return contact ;
}

I'm using a loosely coupled MVC design with Services and Repositories. I've read a lot of posts about this exception but none give me a working answer...

我使用松散耦合的 MVC 设计与服务和存储库。我已经阅读了很多关于这个异常的帖子,但没有一个给我一个有效的答案......

Thank you, Peter

谢谢你,彼得

采纳答案by Peter

[Update]
Because L2E is used you need to save all the linked objects first before you can save the main object. Which makes sense otherwise you would create (in my example) an artist without it's contact object. This isn't allowed by the database design.
[/Update]

[更新]
因为使用L2E,所以需要先保存所有链接的对象,然后才能保存主对象。这是有道理的,否则你会创建(在我的例子中)一个没有联系对象的艺术家。这是数据库设计所不允许的。
[/更新]

Here's my implementation which worked.

这是我的实现。

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id")] Artist artist, [Bind(Prefix = "Contact")] Contact contact, [Bind(Prefix = "Country")] Country country, [Bind(Prefix = "ContactRelationship")] ContactRelationship contactRelationship)
{
    ViewData["Countries"] = new SelectList(new CountryService(_msw).ListCountries().OrderBy(c => c.Name), "ID", "Name");
    ViewData["ContactRelationships"] = new SelectList(new ContactRelationshipService(_msw).ListContactRelationships().OrderBy(c => c.ID), "ID", "Description");

    country = _countryService.GetCountryById(country.ID);
    contact.Country = country;
    contactRelationship = _contactRelationshipService.GetContactRelationship(contactRelationship.ID);
    contact.ContactRelationship = contactRelationship;
    if(_contactService.CreateContact(contact)){
        artist.Contact = contact;
        if (_service.CreateArtist(artist))
            return RedirectToAction("Index");        
    }
    return View("Create");
}

And then in my ContactRepository :

然后在我的 ContactRepository 中:

public Contact CreateContact(Contact contact)
{
    _entities.AddToContact(contact); //no longer throws the exception
    _entities.SaveChanges();
    return contact ;
}

I also found on this website that it is best to keep the same context throughout the application so I'm now using a special Data class for this:

我还在这个网站上发现最好在整个应用程序中保持相同的上下文,所以我现在为此使用一个特殊的 Data 类:

Rick Strahl and Samuel Maecham have taught me that you should keep your datacontext per user per request. Which means putting it in the HttpContext for web applications. Read all about it here

Rick Strahl 和 Samuel Maecham 告诉我,您应该为每个用户的每个请求保留数据上下文。这意味着将它放在 Web 应用程序的 HttpContext 中。在这里阅读所有相关信息

public class Data
{
    public static MyDBEntities MyDBEntities
    {
        get
        {
            if (HttpContext.Current != null && HttpContext.Current["myDBEntities"] == null)
            {
                HttpContext.Current["myDBEntities"] = new MyDBEntities ();
            }
            return HttpContext.Current["myDBEntities"] as MyDBEntities;
        }
        set { 
            if(HttpContext.Current != null)
                HttpContext.Current["myDBEntities"] = value; 
        }
    }
}

回答by bendewey

I've seen this before, you may have to convert the Reference field to an EntityKey before saving and then Load it after its saved. Try this code instead:

我以前见过这个,您可能需要在保存之前将 Reference 字段转换为 EntityKey,然后在保存后加载它。试试这个代码:

public Contact CreateContact(Contact contact){
    contact.ConvertContactRelationToReference();
    _entities.AddToContact(contact); 
    //throws the exception
    _entities.SaveChanges();
    contact.ContactRelation.Load();
    return contact;
}

public partial class Contact
{
  public void ConvertContactRelationToReference()
  {
    var crId = ContactRelation.Id;
    ContactRelation = null;
    ContactRelationReference.EntityKey = new EntityKey("MyEntities.ContactRelations", "Id", crId);
  }
}

Of course, some of this code may need to be changed depending on your exact database structure.

当然,根据您的确切数据库结构,可能需要更改其中一些代码。

回答by user427875

Ummm I wonder if someone can please sanity check my solution. It's very similar to the accepted answer below but after reading Rick Strahl's blog about DataContext Life ManagementI am worried that this isn't a thread-safe solution for a web application.

嗯,我想知道是否有人可以请理智检查我的解决方案。它与下面接受的答案非常相似,但在阅读了 Rick Strahl 的关于DataContext Life Management的博客后,我担心这不是 Web 应用程序的线程安全解决方案。

I also got round the instance where I was receiving this error message by accessing my object context using the singleton pattern.

我还通过使用单例模式访问我的对象上下文来绕过我收到此错误消息的实例。

I added the following to MyObjectContext class:

我在 MyObjectContext 类中添加了以下内容:

// singleton
private static MyObjectContext context;
public static MyObjectContext getInstance()
{
    if (context == null)
    {
        context = new MyObjectContext ();
    }
    return context;
} 

And in my per-entity repository mapper instead of instantiating a new instance of MyObjectContext I use

在我的每个实体存储库映射器中,我使用的不是实例化 MyObjectContext 的新实例

var db = MyObjectContext.getInstance();

Am I being stupid here? It seems to work.

我在这里傻吗?它似乎工作。