简体   繁体   English

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

[英].NET 6 Entity Framework Tracking with Dependency Injection

I guess I just don't understanding EF tracking.我想我只是不了解 EF 跟踪。 I have the context added via dependency injection via:我通过依赖注入添加了上下文:

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));

I set the tracking behavior to NoTracking here (at least so I thought).我在这里将跟踪行为设置为 NoTracking(至少我是这么认为的)。

I have a .NET Controller that has the context in its constructor.我有一个 .NET Controller,它的构造函数中有上下文。 It passes this context to my class constructor containing my methods.它将此上下文传递给包含我的方法的 class 构造函数。 Pretty much everything works fine... except for one:几乎一切正常......除了一个:

I have a method that does a context.RawSqlQuery to get a list of objects.我有一个执行 context.RawSqlQuery 的方法来获取对象列表。 I iterate over these objects calling two separate methods from a different class that was generated the same way (using the injected context).我迭代这些对象,从以相同方式(使用注入上下文)生成的不同 class 调用两个单独的方法。 This method first does a EF query to verify the object does not already exist, if it does it returns it and we move on - no issues.此方法首先执行 EF 查询以验证 object 是否不存在,如果存在则返回它,我们继续 - 没有问题。 On the query to check if it exists I also added.AsNoTracking() for SnGs.在检查它是否存在的查询中,我还为 SnG 添加了 AsNoTracking()。 However, if the object does not exist, and I try to make a new one... every time I do an context.add I get但是,如果 object 不存在,我会尝试创建一个新的......每次我做一个 context.add 我得到

"The instance of entity type 'Whatever' cannot be tracked because another instance with the key value '{MfrId: 90}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached." “无法跟踪实体类型 'Whatever' 的实例,因为已跟踪具有键值 '{MfrId: 90}' 的另一个实例。附加现有实体时,确保只附加一个具有给定键值的实体实例。 “

I have tried adding db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking - no change I have tried adding context.Entry(NewItem).State = EntityState.Detached;我尝试添加 db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking - 没有变化我尝试添加 context.Entry(NewItem).State = EntityState.Detached; before and after the call - no change.通话前后 - 没有变化。 I tried a loop in the context that gets all tracked objects and sets them to detached - no change.我在上下文中尝试了一个循环,该循环获取所有被跟踪的对象并将它们设置为分离 - 没有变化。

What am I missing here?我在这里错过了什么? First - why is it tracking at all?首先 - 它为什么要跟踪? Second - any suggestions on how to get passed this?第二 - 关于如何通过这个的任何建议? Should I just give up using dependency injection for the context (suck... lots of rework for this)?我是否应该放弃对上下文使用依赖注入(糟透了……为此需要大量返工)?

As requested - here is the class & method that is failing (non related stuff removed):根据要求 - 这是失败的 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);
        }
    }

this method is called from this code.从此代码调用此方法。 The OM is also using the injected context 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
             }


So the mfr id is added to a new object that's passed to a pretty much identical methods to create a item (where the mfr id is attached).因此,mfr id 被添加到一个新的 object 中,该 object 被传递给一个几乎相同的方法来创建一个项目(其中附有 mfr id)。 Now - if I detach THAT item - I am ok.现在 - 如果我拆下那个项目 - 我没问题。 But why is it tracking when I have it turned off pretty much everywhere?但是,当我几乎在所有地方都将其关闭时,为什么它仍在跟踪?

Yes, you found your problem.是的,你发现了你的问题。

Turning off Tracking affects what EF does when querying for entities.关闭跟踪会影响 EF 在查询实体时执行的操作。 This means when I tell EF to read data from the DB and give me entities, it will not hang onto references of those entities.这意味着当我告诉 EF 从数据库中读取数据并给我实体时,它不会挂在这些实体的引用上。

However, entities you tell a DBContext to ADD to a DbSet and related entities will be tracked, regardless of your tracking setting.但是,无论您的跟踪设置如何,您告诉 DBContext 添加到 DbSet 的实体和相关实体都将被跟踪。

So if I do something like:所以如果我做类似的事情:

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

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

The references to entity1 and entity2 will be 2 distinct references to the same record.对 entity1 和 entity2 的引用将是对同一记录的 2 个不同引用。 Both are detached, so the DbContext isn't tracking either of them.两者都是分离的,因此 DbContext 不会跟踪它们中的任何一个。 I can use and attach either of them to perform an update, but that entity would be from that point considered Attached until I explicitly detach it again.我可以使用并附加它们中的任何一个来执行更新,但是从那时起该实体将被视为已附加,直到我再次明确分离它。 Attempting to use the other reference for an update would result in that error.尝试使用其他参考进行更新将导致该错误。 If I query specifying NoTracking after I have attached and updated that first entity reference, I will get back a new untracked entity reference.如果我在附加并更新第一个实体引用后查询指定 NoTracking,我将取回一个新的未跟踪实体引用。 The DbContext doesn't return it's tracked reference, but it doesn't discard it either. DbContext 不会返回它的跟踪引用,但它也不会丢弃它。

The exact same thing happens if I add a new entity then query for it specifying NoTracking.如果我添加一个新实体然后查询它并指定 NoTracking,则会发生完全相同的事情。 The query returns an untracked reference.查询返回未跟踪的引用。 So if you try and attach it to update a row, EF will complain about the reference it is already tracking.因此,如果您尝试附加它来更新一行,EF 将抱怨它已经在跟踪的引用。

I don't recommend diving down the rabbit hole of passing around detached entities unless you're keen to spend the time to really understand what is going on behind the scenes and prepared for the pretty deliberate and careful handling of references.我不建议深入绕过分离实体的兔子洞,除非你热衷于花时间真正了解幕后发生的事情,并为非常慎重和谨慎地处理引用做好准备。 The implications aren't just things not working as expected, it's having things work or not work on a completely situational basis which can be a nasty thing to debug and fix, even when you know what to look for.其影响不仅仅是事情没有按预期工作,它是让事情在完全情境的基础上工作或不工作,这可能是一件令人讨厌的调试和修复事情,即使你知道要寻找什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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