【问题标题】:Global setting for AsNoTracking()?AsNoTracking() 的全局设置?
【发布时间】:2012-10-04 12:04:04
【问题描述】:

原来我是这么认为的

context.Configuration.AutoDetectChangesEnabled = false;

将禁用更改跟踪。但不是。目前,我需要在所有 LINQ 查询(对于我的只读层)上使用 AsNoTracking()。是否有全局设置来禁用 DbContext 上的跟踪?

【问题讨论】:

    标签: c# .net entity-framework change-tracking


    【解决方案1】:

    由于这个问题没有用特定的 EF 版本标记,我想提一下,在 EF Core 中,行为可以是 configured at the context level

    您还可以在上下文中更改默认跟踪行为 实例级别:

    using (var context = new BloggingContext())
    {
        context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
    
        var blogs = context.Blogs.ToList();
    }
    

    【讨论】:

    • 如果您有其他设置(例如连接字符串),那么 DbContextOptions 会有所帮助。例如:var dbContextOptionsBuilder = new DbContextOptionsBuilder<BloggingContext>().UseSqlServer("SQL connection to Blogging database").UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);。然后使用var bloggingContext = new BloggingContext(dbContextOptionsBuilder.Options) 创建上下文。
    【解决方案2】:

    如何在派生上下文中简单地公开这样的方法并将其用于查询:

    public IQueryable<T> GetQuery<T>() where T : class {
        return this.Set<T>().AsNoTracking();
    }
    

    无法全局设置AsNoTracking。您必须为每个查询或每个ObjectSet(而不是DbSet)设置它。后一种方法需要使用ObjectContext API。

    var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
    var set = objectContext.CreateObjectSet<T>();
    set.MergeOption = MergeOption.NoTracking;
    // And use set for queries
    

    【讨论】:

    • 当只公开像 GetQuery 这样的单个实体时,如何在实体之间进行连接?不过感谢您的回复。
    • 你可以加入两个不同的GetQuery调用的结果
    • 有可能,但是我需要重做我的通用存储库设置:/ 但是感谢您的建议。
    • @LadislavMrnka 我有一些查询返回未由 DbContext 捕获的类的实例。在这种情况下,我认为 this.Set 不会起作用。另一方面,也许 AsNoTracking 不是必需的?
    【解决方案3】:

    EntityFramework.Core 中非常简单。

    为此,您可以使用UseQueryTrackingBehavior 方法。

    代码 sn-p 在这里:

    services.AddDbContext<DatabaseContext>(options =>
    {
        options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
        options.UseSqlServer(databaseSettings.DefaultConnection);
    });
    

    【讨论】:

      【解决方案4】:

      你可以在你的 DbContext 中做这样的事情:

      public void ObjectContext_OnObjectMaterialized(Object objSender, ObjectMaterializedEventArgs e)
      {
          Entry(e.Entity).State = EntityState.Detached;
      }
      

      每当一个对象被你的上下文具体化时,它就会被分离并且不再被跟踪。

      【讨论】:

      • 我认为这可行,但可能不是最好的方法。 AsNoTracking() 据我所知,不附加和分离对象。
      • 这里的答案详细说明了两者的区别。 stackoverflow.com/a/20163424/219072 听起来 AsNoTracking() 绝对是首选方法。
      【解决方案5】:

      更新:这并没有真正奏效。见 cmets!

      当我在 StackOverflow 上搜索时,我讨厌它,答案是:“你不能!”或“你可以,但前提是你完全改变你曾经拨打的每一个电话。”

      反映任何人?我希望这将是一个 DbContext 设置。但既然不是,我用反射做了一个。

      这个方便的小方法将在 DbSet 类型的所有属性上设置 AsNoTracking。

          private void GloballySetAsNoTracking()
          {
              var dbSetProperties = GetType().GetProperties();
              foreach (PropertyInfo pi in dbSetProperties)
              {
                  var obj = pi.GetValue(this, null);
                  if (obj.GetType().IsGenericType && obj.GetType().GetGenericTypeDefinition() == typeof(DbSet<>))
                  {
                      var mi = obj.GetType().GetMethod("AsNoTracking");
                      mi.Invoke(obj, null);
                  }
              }
          }
      

      将其添加到重载的 DbContext 构造函数中。

          public ActivationDbContext(bool proxyCreationEnabled, bool lazyLoadingEnabled = true, bool asNoTracking = true)
          {
              Configuration.ProxyCreationEnabled = proxyCreationEnabled;
              Configuration.LazyLoadingEnabled = lazyLoadingEnabled;
              if (asNoTracking)
                  GloballySetAsNoTracking();
          }
      

      它使用反射,这意味着有人会很快评论这是对性能的影响。但它真的那么受欢迎吗?取决于您的用例。

      【讨论】:

      • 我没有对此进行测试,但据我所知,AsNoTracking() 仅将当前集作为未跟踪的IQueryable 返回。在DbSet 上调用它不会使下一个查询不被跟踪。我从这段代码中了解到,您在所有集合上都调用 AsNoTracking(),但除非您将返回的可查询对象用于任何内容,否则这不会做任何事情
      • @Jcl,我得检查一下。它似乎对我有用。
      • 我现在没有时间测试它,但乍一看它看起来很可疑。如果这可行,则意味着无论何时您在上下文中对DbSet 调用AsNoTracking(),在同一上下文中对该DbSet 的所有后续查询都将不被跟踪......奇怪的行为(特别考虑到没有AsTracking() 可以补偿)。如果这确实有效,我会说这是一个错误......或未记录的功能:-)
      • @Rhyous 我不认为设置 AsNoTracking() 一次会永远设置它,根据这个链接。 c-sharpcorner.com/UploadFile/ff2f08/…我看到这已经是近一年的评论了。如果您有任何替代方法来设置 Globally AsNoTracking,请告诉我
      • 请原谅我忘记发布我的发现。正如@jcl 所说,它是每个查询。新查询不支持 AsNoTracking() 命令。我在 EF(存储库模式)周围实现了一个包装器,现在需要 AsNoTracking 的调用有了它。
      【解决方案6】:

      就我而言,因为我需要将整个上下文设为只读而不是读/写。

      所以我对 tt 文件进行了更改,并将所有 DbContext 属性更改为返回 DbQuery 而不是 DbSet,从所有属性中删除了集合,对于获取,我返回了 Model.AsNoTracking()

      例如:

      public virtual DbQuery&lt;Campaign&gt; Campaigns { get{ return Set&lt;Campaign&gt;().AsNoTracking();} }

      我在tt模板中这样做的方式是:

      public string DbQuery(EntitySet entitySet)
          {
              return string.Format(
                  CultureInfo.InvariantCulture,
                  "{0} virtual DbQuery<{1}> {2} {{ get{{ return Set<{1}>().AsNoTracking();}} }}",
                  Accessibility.ForReadOnlyProperty(entitySet),
                  _typeMapper.GetTypeName(entitySet.ElementType),
                  _code.Escape(entitySet));
          }

      【讨论】:

        【解决方案7】:

        如果使用实体框架核心,您还可以在继承 DbContext 的类的构造函数中添加以下代码。

        public NPCContext()
                : base()
        {
             base.ChangeTracker.AutoDetectChangesEnabled = false;
        }
        

        或以下

            public NPCContext(DbContextOptions<NPCContext> options)
                : base(options)
            {
                base.ChangeTracker.AutoDetectChangesEnabled = false;
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-02
          • 2020-09-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多