繁体   English   中英

.NET 6 使用依赖注入的实体框架跟踪

[英].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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM