. net 6实体框架跟踪与依赖注入



我想我只是不理解EF跟踪。我通过依赖注入添加了上下文:

builder.Services.AddDbContext<OracleContext>(options => options.UseOracle(OracleConnectionString, b => b.UseOracleSQLCompatibility("11"))
.LogTo(s => System.Diagnostics.Debug.WriteLine(s))
.EnableDetailedErrors(Settings.Dev_System)
.EnableSensitiveDataLogging(Settings.Dev_System)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));

我在这里将跟踪行为设置为NoTracking(至少我是这样认为的)。

我有一个。net控制器,它的构造函数中有上下文。它将这个上下文传递给包含我的方法的类构造函数。几乎一切都很好……除了一个:

我有一个做context的方法。RawSqlQuery获取对象列表。我遍历这些对象,调用以相同方式生成的不同类(使用注入的上下文)的两个独立方法。这个方法首先做一个EF查询来验证对象是否已经存在,如果已经存在,它就返回对象,然后我们继续——没有问题。在检查它是否存在的查询中,我还为sng添加了. asnotracking()。但是,如果对象不存在,而我试图创建一个新的对象……每次我做一个上下文。add I get

"实体类型为'Whatever'的实例无法被跟踪,因为键值为'{MfrId: 90}'的另一个实例已经被跟踪。在附加现有实体时,确保只附加一个具有给定键值的实体实例。">

我试过添加db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior。没有跟踪-没有变化我试过添加context.Entry(NewItem)。状态= EntityState.Detached;通话前后都没有变化。我尝试在上下文中循环获取所有跟踪对象并将它们设置为分离-没有变化。

我在这里错过了什么?首先,它为什么要跟踪?第二,关于如何通过考试有什么建议吗?我应该放弃在上下文中使用依赖注入吗?这需要大量的返工)?

按要求-这是类&方法失败(删除不相关的内容):

public class AssetMethods : IAssetMethods
{
public OracleContext db; 

public AssetMethods(OracleContext context)
{
db = context;   
}

public CcpManufacturer? CreateNewManufacturer(CcpManufacturer NewMan, string ActorID)
{
...blah blah non DB validation stuff removed...
//Check if it exists already
CcpManufacturer? OldMan = db.CcpManufacturers.Where(m=>m.MfrName == NewMan.MfrName).AsNoTracking().FirstOrDefault();
if (OldMan != null) { 
return OldMan;
}
//Who done did it
NewMan.CreatedBy = ActorID;
NewMan.CreationDate = DateTime.Now;
NewMan.Isenabled = true;            
//save                         
db.CcpManufacturers.Add(NewMan);           
db.SaveChanges(); //fails here
//Prevent XSS Reflection
return db.CcpManufacturers.Find(NewMan.MfrId);
}
}

此方法从此代码调用。OM也在使用注入的上下文

List<MasterItem> Items = OM.RawSqlQuery(Query, x => new MasterItem { MFR_NAME = (string)x[0], MODEL_NUMBER = (string)x[1], LEGAL_NAME= (string)x[2]});
foreach (MasterItem item in Items)
{
CcpManufacturer? Man = new() {
MfrName = item.MFR_NAME,
Displayname = item.MFR_NAME
};
Man = AM.CreateNewManufacturer(Man,System.Id); //if its new.. it never get passed here because of the discussed error...
if (Man == null || Man.MfrId == 0)
{
continue;
}
.... other stuff
}

因此,mfr id被添加到一个新对象中,该对象被传递给一个几乎相同的方法来创建一个项目(其中附加了mfr id)。现在,如果我把那件事分开,我就没事了。但为什么我把所有地方都关掉了它还在跟踪呢?

是的,你找到你的问题了。

关闭跟踪会影响EF在查询时的操作表示实体。这意味着当我告诉EF从DB读取数据并给我实体时,它不会挂起这些实体的引用。

但是,无论您的跟踪设置如何,您告诉DBContext添加到DbSet的实体和相关实体都将被跟踪。

所以如果我这样做:

var entity1 = context.Entities.Single(x => x.Id == entityId).AsNoTracking();
var entity2 = context.Entities.Single(x => x.Id == entityId).AsNoTracking();

对entity1和entity2的引用将是对同一条记录的两个不同引用。两者都是分离的,因此DbContext不跟踪它们中的任何一个。我可以使用并附加它们中的任何一个来执行更新,但从那时起,该实体将被认为是附加的,直到我再次显式地分离它。尝试使用其他引用进行更新将导致该错误。如果我在附加并更新了第一个实体引用之后查询指定NoTracking,我将得到一个新的未跟踪实体引用。DbContext不返回它所跟踪的引用,但也不会丢弃它。

如果我添加一个新实体,然后查询它指定NoTracking,则会发生完全相同的事情。查询返回一个未跟踪的引用。因此,如果您尝试将其附加到更新行,EF将抱怨它已经跟踪的引用。

我不建议你陷入传递独立实体的兔子洞,除非你渴望花时间真正了解幕后发生了什么,并为相当慎重和小心地处理引用做好准备。它的含义不仅仅是事情没有像预期的那样工作,它是在完全情境的基础上让事情工作或不工作,这可能是一个讨厌的事情,以调试和修复,即使你知道要寻找什么。

最新更新