gpt4 book ai didi

c# - 如何找到EF CodeFirst CTP5中针对更改进行跟踪的所有对象的来源?

转载 作者:行者123 更新时间:2023-11-30 18:05:41 27 4
gpt4 key购买 nike

我遇到“一个IEntityChangeTracker的多个实例无法引用一个实体对象”问题。经过一番检查之后,看来我有一个正在跟踪更改的对象。问题是我不知道问题对象的来源……它显然已经放到上下文中,但是我不确定哪个调用没有正确分离。

因此,经过数小时的尝试,我正在寻找如何在树上行走以查找与之冲突的源对象,因为这也许可以帮助我了解源对象的添加位置。

该错误正在第226行引发,因此看起来我要么已有一个“隐形”客户,要么可能是客户的一个属性导致了此错误,因为客户有几个其他属性,它们是它们自己的复杂对象类型。 ..

Line 224:                    if (null != this.Customer)
Line 225: {
Line 226: context.Entry(this.Customer).State = EntityState.Unchanged;
Line 227: }


该错误并未说明是哪个对象引起了错误,它仅指向第226行。假设是导致该问题的幻影客户对象,我尝试了:

        var test = ((IObjectContextAdapter)dataContext).ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged);

foreach(var e in test)
{
if(e.GetType() == typeof(Customer))
{
dataContext.Detach(e);
}
}


想法是遍历拥有对所有对象的引用的事物,希望找到顽皮的Customer并为其提供引导。但是,a,这行不通;在此循环中找不到客户。哦,仅供参考-这是在上一个代码之前运行了几行,因此我不会在这里进行任何额外的对象创建。

因此,我需要一种方法来确定实际上是哪个对象导致了错误。

@Ladislav-仅供参考-我有一个公共库,其中包含所有业务对象(BO)。这个公共库供其他项目使用-Windows Service,Web Service等。我试图使每个BO负责填充和保存自身,因此我没有一个hugo数据访问类。每个BO都负责自己的Save()方法。这是当前saveUpdate方法的示例:

    public void SaveOrUpdate(DataContext context)
{
if (context.Entry(this).State == EntityState.Detached)
{
context.Customers.Add(this);
context.SaveChanges();
}
else //update
{
context.Entry(this).State = System.Data.EntityState.Modified;
context.SaveChanges();
}
}


关于您对范围的建议,我尝试了各种策略-乍一看每个人都说原子地做到这一点-所以我让每种方法都抓住了DataContext的新实例来完成它的工作。只要对象不是很复杂并且彼此不依赖,即只包含基本类型属性(如int和string),这就可以很好地工作。

但是,一旦我开始遇到这些并发错误,我便将其挖掘出来,发现即使将DataContext丢弃后,它仍会以某种方式保留对对象的引用。这是一个疯狂的坏工程,IMHO。即,因此,如果我将客户BO添加到DataContext中,然后允许DataContext超出范围并被Dispose,然后旋转一个新的DataContext以执行某些操作,则原始的Customer BO指针仍然存在!

所以我在StackOverflow上读了一堆(我可能会补充您很多答案), Rick Strahl's treatise on DataContext Lifetime Management8 Entity Framework Gotchas by Julia Lerman

因此,朱莉亚(Julia)说放入了Dispose方法,我做了,但没有帮助,DataContext仍然神奇地保留了引用。

因此,Rick说,尝试使用“全局” DataContext,这样您就只需要担心一个DataContext,并且它应该知道正在发生的一切,所以它不会踩自己的脚。但这似乎也不起作用。公平地说,Rick谈论的是Linq to SQL和一个Web应用程序,但我有点希望它也适用于我。

然后,各种答案都表明您不希望使用GlobalDataContext,因为它会变得非常大,非常快,因为它拥有有关所有对象的所有信息,因此只需将DataContext用于工作单元即可。

好吧,我已经分解了一个工作单元,以表示要一起完成的一组对象的所有更改,添加和更新。因此,对于我的示例,下面是一些BO和属性:

留言组
-属性:清单
-物业:客户

顾客
-属性:清单
-属性:清单

信息
-物业:客户
-属性:MessageGroup
-属性:用户

用户
-物业:客户
-属性:清单

在系统中,当MessageGroup到达时(如Xml),将对其进行检查和解析。 MessageGroup构造函数使用了依赖注入,并将DataContext作为其参数之一-因此,所有正在创建的“子” BO都使用DataContext的这一实例。从数据库中获取客户(或创建一个新客户)并将其分配给MessageGroup ...让我们假设它是一个现有客户-因此无需对其进行任何更新,它是DataContext中的新鲜事物。

然后,MessageGroup.Messages列表被循环,并且要创建的第一个子BO是一个新的User对象。我将相同的Customer对象(来自MessageGroup)分配给User。但是,当调用context.Users.Add(this)时,出现错误。如果未将客户分配给用户,则不会收到错误。

因此,现在我有了一个来自数据库的新鲜客户(或不确定的子资产),不需要跟踪就可以引起我的焦虑。我以为可以将其从上下文中删除,例如:

var cust = Customer.GetCustomerFromExternalId(crm.CustomerId);
dataContext.Detach(cust);
dataContext.SaveChanges();


但是,即使我已明确将其删除,我仍然会收到该错误。当然,如果它是Customer的子属性之一,也许还没有删除?

目前,我想知道存储库模式是否适合我的目的。我也想知道EF CodeFirst是根本缺陷还是过于复杂?也许我应该改用SubSonic或NHibernate?

最佳答案

据我所知,可能没有明确的方法可从POCO实体获取相关上下文-动态代理的所有相关属性都是非公开的。要检查DbContext中的实体,请使用:

context.ChangeTracker.Entries<Customer>().Where(e => e.State == ...)


避免问题的最佳方法是每个“工作单元”使用单个上下文。如果您有来自多个上下文的实体,显然您就不会采用这种方法。此外,您似乎正在使用多个并发的活动上下文。

关于c# - 如何找到EF CodeFirst CTP5中针对更改进行跟踪的所有对象的来源?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5318903/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com