[英].NET 6 Entity Framework Tracking with Dependency Injection
我想我只是不了解 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 Controller,它的构造函数中有上下文。 它将此上下文传递给包含我的方法的 class 构造函数。 几乎一切正常......除了一个:
我有一个执行 context.RawSqlQuery 的方法来获取对象列表。 我迭代这些对象,从以相同方式(使用注入上下文)生成的不同 class 调用两个单独的方法。 此方法首先执行 EF 查询以验证 object 是否不存在,如果存在则返回它,我们继续 - 没有问题。 在检查它是否存在的查询中,我还为 SnG 添加了 AsNoTracking()。 但是,如果 object 不存在,我会尝试创建一个新的......每次我做一个 context.add 我得到
“无法跟踪实体类型 'Whatever' 的实例,因为已跟踪具有键值 '{MfrId: 90}' 的另一个实例。附加现有实体时,确保只附加一个具有给定键值的实体实例。 “
我尝试添加 db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking - 没有变化我尝试添加 context.Entry(NewItem).State = EntityState.Detached; 通话前后 - 没有变化。 我在上下文中尝试了一个循环,该循环获取所有被跟踪的对象并将它们设置为分离 - 没有变化。
我在这里错过了什么? 首先 - 它为什么要跟踪? 第二 - 关于如何通过这个的任何建议? 我是否应该放弃对上下文使用依赖注入(糟透了……为此需要大量返工)?
根据要求 - 这是失败的 class 和方法(删除了不相关的内容):
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 被添加到一个新的 object 中,该 object 被传递给一个几乎相同的方法来创建一个项目(其中附有 mfr id)。 现在 - 如果我拆下那个项目 - 我没问题。 但是,当我几乎在所有地方都将其关闭时,为什么它仍在跟踪?
是的,你发现了你的问题。
关闭跟踪会影响 EF 在查询实体时执行的操作。 这意味着当我告诉 EF 从数据库中读取数据并给我实体时,它不会挂在这些实体的引用上。
但是,无论您的跟踪设置如何,您告诉 DBContext 添加到 DbSet 的实体和相关实体都将被跟踪。
所以如果我做类似的事情:
var entity1 = context.Entities.Single(x => x.Id == entityId).AsNoTracking();
var entity2 = context.Entities.Single(x => x.Id == entityId).AsNoTracking();
对 entity1 和 entity2 的引用将是对同一记录的 2 个不同引用。 两者都是分离的,因此 DbContext 不会跟踪它们中的任何一个。 我可以使用并附加它们中的任何一个来执行更新,但是从那时起该实体将被视为已附加,直到我再次明确分离它。 尝试使用其他参考进行更新将导致该错误。 如果我在附加并更新第一个实体引用后查询指定 NoTracking,我将取回一个新的未跟踪实体引用。 DbContext 不会返回它的跟踪引用,但它也不会丢弃它。
如果我添加一个新实体然后查询它并指定 NoTracking,则会发生完全相同的事情。 查询返回未跟踪的引用。 因此,如果您尝试附加它来更新一行,EF 将抱怨它已经在跟踪的引用。
我不建议深入绕过分离实体的兔子洞,除非你热衷于花时间真正了解幕后发生的事情,并为非常慎重和谨慎地处理引用做好准备。 其影响不仅仅是事情没有按预期工作,它是让事情在完全情境的基础上工作或不工作,这可能是一件令人讨厌的调试和修复事情,即使你知道要寻找什么。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.