我想我只是不理解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查询以验证对象是否已经存在,如果有,它返回它,我们继续前进-没有问题。在查询检查它是否存在时,我还添加了。AsNoTracking()。但是,如果对象不存在,并且我尝试创建一个新对象......每次创建上下文时。add我会得到
“无法跟踪实体类型”Whatever“的示例,因为另一个具有键值”{MfrId:“90}”已被跟踪。附加现有实体时,请确保仅附加一个具有给定键值的实体示例。
我已经尝试添加数据库。ChangeTracker。QueryTrackingBehavior = QueryTrackingBehavior。NoTracking -没有更改我已经尝试添加上下文。Entry(NewItem)。State = EntityState。已分离;在调用之前和之后-没有变化。我在上下文中尝试了一个循环,它获取所有被跟踪的对象并将它们设置为分离-没有变化。
我在这里错过了什么?首先-为什么它要跟踪?其次-关于如何通过这个有什么建议吗?我应该给予使用上下文的依赖注入吗?(糟糕...为此做了很多工作)?
根据要求-以下是失败的类和方法(删除了不相关的内容):
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
}
1条答案
按热度按时间vlju58qv1#
因此,mfr id被添加到一个新对象中,该对象被传递给一个几乎相同的方法来创建一个项目(mfr id被附加在其中)。现在,如果我分离那个项目,我是可以的。但为什么它会跟踪,而我几乎在所有地方都关闭了它?
是的,你找到你的问题了。
关闭跟踪会影响EF在***查询***实体时的行为。这意味着当我告诉EF从数据库读取数据并给予实体时,它不会挂起对这些实体的引用。
但是,无论您的跟踪设置如何,都将跟踪您通知DBContext添加到DbSet的实体和相关实体。
所以如果我这样做:
对entity1和entity2的引用将是对同一记录的两个不同的引用。这两个引用都是分离的,因此DbContext不会跟踪它们中的任何一个。我可以使用并附加它们中的任何一个来执行更新,但在我再次显式分离它之前,该实体将被视为Attached。如果在附加并更新第一个实体引用后指定NoTracking进行查询,则会返回一个新的未跟踪实体引用。DbContext不会返回其跟踪的引用,但也不会丢弃它。
如果添加一个新实体,然后指定NoTracking对其进行查询,则会发生完全相同的情况。查询返回一个未跟踪的引用。因此,如果尝试附加它以更新行,EF将抱怨它已经跟踪的引用。
我不建议你钻到传递分离实体的兔子洞里去,除非你想花时间真正理解幕后发生的事情,并为相当深思熟虑和小心地处理引用做好准备。这意味着不仅仅是事情没有按预期工作,而是在完全的情境基础上让事情工作或不工作,这可能是一件很难调试和修复的事情。即使你知道要找什么。