【问题标题】:Solution to "An entity object cannot be referenced by multiple instances of IEntityChangeTracker" error“一个实体对象不能被多个IEntityChangeTracker实例引用”错误的解决方法
【发布时间】:2013-11-18 17:27:12
【问题描述】:

我正在尝试更新一个有很多关系的成员,但收到一条错误消息,指出 An entity object cannot be referenced by multiple instances of IEntityChangeTracker。以下是我编写的各种模型和控制器。任何有关如何纠正此问题的建议将不胜感激。

会员模型

public partial class Member
  {
    NCOWW_DB db = new NCOWW_DB();
    public Member()
    {
      this.Contacts = new HashSet<Contact>();
      this.Utilities = new HashSet<Utility>();
    }

    public int MemberID { get; set; }

    [Display(Name = "Member Name")]
    [Required]
    public string Name { get; set; }

    [Display(Name = "Member Number")]
    public string Number { get; set; }

    [Display(Name = "Mutual Aid Agreement On File")]
    public bool MutualAid { get; set; }

    [Display(Name = "Type ID")]
    public Nullable<int> TypeID { get; set; }

    [ForeignKey("TypeID")]
    public virtual UtilityType MemberType { get; set; }

    [Display(Name = "Water Population")]
    public Nullable<int> WaterPopulation { get; set; }

    [Display(Name = "Wastewater Population")]
    public Nullable<int> WastewaterPopulation { get; set; }

    private Address _Address = new Address();
    public Address Address { get { return _Address; } set { _Address = value; } }

    private Coordinates _Coordinates = new Coordinates();
    public Coordinates Coordinates { get { return _Coordinates; } set { _Coordinates = value; } }

    [NotMapped]
    public double Distance { get; set; }

    public bool Deleted { get; set; }

    [Display(Name = "Enable Member")]
    public bool Enabled { get; set; }

    private DateTime _createdOn = DateTime.UtcNow;
    public DateTime CreatedOn { get { return _createdOn; } set { _createdOn = value; } }

    private DateTime _modifiedOn = DateTime.UtcNow;
    public DateTime ModifiedOn { get { return _modifiedOn; } set { _modifiedOn = value; } }

    public System.Guid ModifiedBy { get; set; }

    //public virtual ICollection<Utility> Contacts { get; set; }
    private ICollection<Contact> _Contacts;
    public virtual ICollection<Contact> Contacts {
      get
      {
        return (from c in db.Contact
               where (c.MemberID == MemberID && c.Deleted == false)
               select c).OrderBy(c => c.FirstName).ToList(); 
      }
      set
      {
        _Contacts = value;
      }
    }

    //public virtual ICollection<Utility> Utilities { get; set; }
    private ICollection<Utility> _Utilities;
    public virtual ICollection<Utility> Utilities
    {
      get
      {
        return (from u in db.Utility
                where (u.MemberID == MemberID && u.Deleted == false)
                select u).OrderBy(u => u.Name).ToList();
      }
      set
      {
        _Utilities = value;
      }
    }
  }

效用模型

  public partial class Utility
  {
    NCOWW_DB db = new NCOWW_DB();
    public Utility()
    {
      this.Contacts = new HashSet<Contact>();
      this.Resources = new HashSet<UtilityResource>();
    }
    [Key]
    public int UtilityID { get; set; }
    [Display(Name="Member")]
    [Required]
    public int MemberID { get; set; }
    [ForeignKey("MemberID")]
    public virtual Member Member { get; set; }
    [ForeignKey("UtilityID")]
    public virtual ICollection<UtilityResource> Resources { get; set; }
    [ForeignKey("UtilityID")]
    private ICollection<Contact> _Contacts;
    public virtual ICollection<Contact> Contacts
    {
      get
      {
        return (from c in db.Contact
                where (c.MemberID == MemberID && c.Deleted == false)
                select c).OrderBy(c => c.FirstName).ToList();
      }
      set
      {
        _Contacts = value;
      }
    }
    //public virtual ICollection<Contact> Contacts { get; set; }
    [Required]
    public string Name { get; set; }
    private Address _Address = new Address();
    public Address Address { get { return _Address; } set { _Address = value; } }
    private Coordinates _Coordinates = new Coordinates();
    public Coordinates Coordinates { get { return _Coordinates; } set { _Coordinates = value; } }
    [Display(Name = "Utility Type")]
    public int? TypeID { get; set; }
    [ForeignKey("TypeID")]
    [Display(Name = "Utility Type")]
    public virtual UtilityType UtilityType { get; set; }
    [Display(Name = "Region")]
    public int? RegionID { get; set; }
    [Display(Name = "County")]
    public int? CountyID { get; set; }
    private UtilityInfo _WaterInfo = new UtilityInfo();
    [Display(Name="Water Information")]
    public UtilityInfo WaterInfo { get { return _WaterInfo; } set { _WaterInfo = value; } }
    private UtilityInfo _WastewaterInfo = new UtilityInfo();
    [Display(Name = "Wastewater Information")]
    public UtilityInfo WastewaterInfo { get { return _WastewaterInfo; } set { _WastewaterInfo = value; } }
    [NotMapped]
    public double Distance { get; set; }
    private bool _enabled = true;
    public bool Enabled { get { return _enabled; } set { _enabled = value; } }
    public bool Deleted { get; set; }
    private DateTime _createdOn = DateTime.UtcNow;
    public DateTime CreatedOn { get { return _createdOn; } set { _createdOn = value; } }
    private DateTime _modifiedOn = DateTime.UtcNow;
    public DateTime ModifiedOn { get { return _modifiedOn; } set { _modifiedOn = value; } }
    public Guid ModifiedBy { get; set; }
  }

联系模特

  public partial class Contact
  {
    public Contact()
    {
      this.Phones = new HashSet<ContactPhone>();
    }
    [Key]
    public int ContactID { get; set; }
    public Nullable<Guid> UserID { get; set; }
    [Display(Name = "Member")]
    public int MemberID { get; set; }
    public Nullable<int> UtilityID { get; set; }
    public string Username { get; set; }

    [Display(Name = "First Name")]
    [Required]
    public string FirstName { get; set; }
    [Display(Name = "Last Name")]
    [Required]
    public string LastName { get; set; }
    [NotMapped]
    public string FullName { get { return FirstName + " " + LastName; } }
    [NotMapped]
    public string FullNameLastNameFirst { get { return LastName + ", " + FirstName; } }
    [Display(Name = "Contact Type")]
    public string Type { get; set; }
    [RegularExpression(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")]
    [Required]
    public string Email { get; set; }

    [ForeignKey("ContactID")]
    public virtual ICollection<ContactPhone> Phones { get; set; }

    [NotMapped]
    [Display(Name = "Grant Website Access")]
    public bool WebsiteAccess { get; set; }

    public bool Deleted { get; set; }
    private DateTime _createdOn = DateTime.UtcNow;
    public DateTime CreatedOn { get { return _createdOn; } set { _createdOn = value; } }

    private DateTime _modifiedOn = DateTime.UtcNow;
    public DateTime ModifiedOn { get { return _modifiedOn; } set { _modifiedOn = value; } }
    public System.Guid ModifiedBy { get; set; }

    public virtual Member Member { get; set; }
    public virtual Utility Utility { get; set; }

    [NotMapped]
    public string Password { get; set; }
    [NotMapped]
    [Compare("Password")]
    [Display(Name = "Confirm Password")]
    public string PasswordConfirm { get; set; }
  }
  public partial class ContactPhone
  {
    [Key]
    public int PhoneID { get; set; }
    public int ContactID { get; set; }
    public string Number { get; set; }
    public string Type { get; set; }
  }

成员编辑控制器

    private NCOWW_DB db = new NCOWW_DB(); //dbContext
    public ActionResult Edit(Member member)
    {
      try
      {
        if (ModelState.IsValid)
        {
          member.ModifiedBy = CurrentUser;
          member.ModifiedOn = DateTime.UtcNow;
          db.Entry(member).State = EntityState.Modified; <--ERROR OCCURS HERE!
          db.SaveChanges();

          //RETURN TO DASHBOARD
          return RedirectToAction("Index");
        }
      }
      catch (Exception err)
      {
        /*catch error code*/
      }
      return View(member);
    }

更新

根据下面的答案,我已经修改了我的代码,试图删除错误的NCOWW_DB db = new NCOWW_DB()。但是,这并不能解决问题,因为我不确定如何正确传递dbContext。无论如何,这就是代码当前所在的位置:

更新的成员模型

    ...
    private ICollection<Contact> _Contacts;
    public virtual ICollection<Contact> Contacts {
      get
      {
        return MemberFunctions.LoadMemberContacts(MemberID);
      }
      set
      {
        _Contacts = value;
      }
    }

    private ICollection<Utility> _Utilities;
    public virtual ICollection<Utility> Utilities
    {
      get
      {
        return MemberFunctions.LoadMemberUtilities(MemberID);
      }
      set
      {
        _Utilities = value;
      }
    }
  }
  public static class MemberFunctions
  {
    public static ICollection<Contact> LoadMemberContacts(int memberID)
    {
      NCOWW_DB db = new NCOWW_DB();
      return (from c in db.Contact
              where (c.MemberID == memberID && c.UtilityID == null && c.Deleted == false)
              select c).OrderBy(c => c.FirstName).ToList(); 
    }
    public static ICollection<Utility> LoadMemberUtilities(int memberID)
    {
      NCOWW_DB db = new NCOWW_DB();
      return (from u in db.Utility
              where (u.MemberID == memberID && u.Deleted == false)
              select u).OrderBy(u => u.Name).ToList();
    }
  }

【问题讨论】:

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


    【解决方案1】:

    您在处理多个上下文时非常粗心(甚至没有正确处理)。在每个 MemberUtility 实体中,您都会实例化一个新的上下文:

    NCOWW_DB db = new NCOWW_DB();
    

    并在像Member.Contacts 这样的属性获取器中使用它(如果集合尚未加载,即使没有保护,所以每次访问属性时都会得到一个数据库查询)。

    出现您的特定异常是因为db.Entry(member).State = EntityState.Modified 会将member 实体和所有相关实体附加到上下文db。为了附加相关的Contact 实体,EF 必须接触属性 getter member.Contacts,它将在你的 getter 中运行查询,将Contact 实体附加到另一个上下文,即你在 member 实体中实例化的那个.不允许将同一个对象附加到两个不同的上下文,这会导致异常。

    您应该以某种方式重构您的代码,以确保您只将相同的上下文实例用于数据库操作。特别是我会远离在实体内创建上下文成员。存储库或数据访问或服务层或任何您喜欢的名称是此类查询的更好位置。

    【讨论】:

    • 感谢斯劳马的信息。我想可能是这样的。关于存储库/数据访问/服务层信息,您能指出我正确的方向吗?
    • @QuantumDynamix:这本身就是一个更大的话题。基本上,我的意思是一些单独的类,它引用了您在控制器中使用的上下文db(将其作为参数传递给此类的构造函数)和诸如LoadMemberContacts(member)之类的方法,它将运行您现在拥有的查询在属性 getter 中并将结果设置为member.Contacts(或使用显式加载)。避免问题中出现异常的重要事项:在单个请求中始终使用相同的 db 实例!
    • 我找到了this page online。这是开始研究这个概念的好地方吗?
    • @QuantumDynamix:我不知道,它可能有比要求更多的抽象,我不想让你陷入不必要的复杂之中。 (只需去掉 entities 中的上下文成员!)但您当然可以尝试...
    • 好吧,我很确定我现在正在关注你。但是,如何将错误的 NCOWW_DB db = new NCOWW_DB(); 从模型中剥离出来并放入构造函数中,以便可以正确地将其传递给各种模型?
    【解决方案2】:

    当您设置成员的属性时,它会自动获取修改状态 so 语句 不需要db.Entry(member).State = EntityState.Modified;

    但我没有看到您将实体附加到 dbcontext 的位置,这可能是您代码中的另一个问题。 看来你应该改为打电话

    db.Members.Attach(member);
    

    【讨论】:

    • 感谢基里尔的建议。但是,删除该行会导致数据库中没有数据被更新。
    • 您的问题似乎是您的实体未附加到上下文。我更新答案
    猜你喜欢
    • 1970-01-01
    • 2011-11-01
    • 2011-10-12
    • 1970-01-01
    相关资源
    最近更新 更多