实体框架和实体跟踪器问题

时间:2009-06-16 20:58:26

标签: entity-framework

如果我运行以下代码,则会抛出以下错误:

IEntityChangeTracker的多个实例

无法引用实体对象
 public void Save(Category category)
        {
            using(var db = new NorthwindContext())
            {
                if(category.CategoryID == 0) 
                {
                    db.AddToCategorySet(category); 
                }

                else
                {
                    //category.RemoveTracker();
                    db.Attach(category);
                }

                db.SaveChanges(); 
            }
        } 

原因当然是从我们从GetById方法获得的接口发送类别,该方法已经将EntityChangeTracker附加到类别对象。我还尝试将实体跟踪器设置为null,但它没有更新类别对象。

  protected void Btn_Update_Category_Click(object sender, EventArgs e)
        {
            _categoryRepository = new CategoryRepository();
            int categoryId = Int32.Parse(txtCategoryId.Text);

            var category = _categoryRepository.GetById(categoryId);

            category.CategoryName = txtUpdateCategoryName.Text; 

            _categoryRepository.Save(category);
        }

1 个答案:

答案 0 :(得分:0)

我自己还在学习实体框架,但也许我可以帮助一点。使用Entity Framework时,您需要了解如何处理不同的上下文。看起来您正试图通过以下方式尽可能地本地化您的上下文:

public void Save(Category category)
{
    using (var db = new NorthwindContext())
    {
        ...
    }
}

...在您的数据访问方法中。你在GetById方法中做了同样的事吗?如果是这样,你是否记得将你收回的物品分开,以便以后可以在不同的背景下附上?

public Category GetById(int categoryId)
{
    using (var db = new NorthwindContext())
    {
        Category category = (from c in db.Category where Category.ID == categoryId select c).First();
        db.Detach(category);
    }
}

当你拨打Attach时,它并没有试图踩到已经附加的上下文。这有帮助吗?

正如您在评论中指出的那样,当您尝试修改项目然后告诉数据库层保存它时会出现问题,因为一旦项目与其上下文分离,它就不再跟踪对它做出的改变。我可以通过几种方式来解决这个问题,但这些方法都不完美。

  1. 如果您的架构支持它,您可以扩展上下文的范围,使Save方法可以使用GetById方法使用的相同上下文。这有助于完全避免整个附加/分离问题,但它可能会使您的数据层更接近您的业务逻辑而不是您想要的。
  2. 您可以根据其ID从新上下文中加载项目的新实例,根据传入的类别设置其所有属性,然后保存。这需要花费两次数据库往返,因为它实际上只需要一次,并且它不是很容易维护。
  3. 您可以深入了解上下文本身,将类别的属性标记为已更改。
  4. 例如:

    public void Save(Category category)
    {
        using (var db = new NorthwindContext())
        {
            db.Attach(category);
            var stateEntry = db.ObjectStateManager.GetObjectStateEntry(category);
            foreach (var propertyName in stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(fm => fm.FieldType.Name)) {
                stateEntry.SetModifiedProperty(propertyName);
            }
            db.SaveChanges();
        }
    }
    

    这看起来有点丑陋,但整体上应该更具高效性和可维护性。另外,如果你愿意,你可以使它足够通用,可以在某个地方投入扩展方法,这样你就不必看到或重复丑陋的代码了,但你仍然可以从中获得它的功能。