2012-10-04 133 views
43

本来我认为AsNoTracking()的全局设置?

context.Configuration.AutoDetectChangesEnabled = false; 

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

回答

14

由于此问题未使用特定的EF版本进行标记,因此我想提及在EF Core中的行为可能是configured at the context level

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

using (var context = new BloggingContext()) 
{ 
    context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; 

    var blogs = context.Blogs.ToList(); 
} 
+0

你的链接告诉我错误 –

+0

@AliYousefie谢谢你,我修正了这个问题。 –

33

什么根本上派生的背景下暴露的方法像这样利用它进行查询:

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 
+1

会怎么做一个在Entit之间加入是否只暴露像GetQuery 这样的单个实体?感谢您的回复。 – Vindberg

+0

你可以加入两个不同'GetQuery'调用的结果 –

+0

它可能,但是我需要重做我的通用资源库设置:/但是谢谢你的建议。 – Vindberg

2

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

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

每当一个对象被你的情况下实现,它将被分离并不再跟踪。

+0

我认为这可行,但也许不是最好的方式来做到这一点。 AsNoTracking()据我所知不附加和分离对象。 –

+0

这里的答案阐述了两者之间的区别。 http://stackoverflow.com/a/20163424/219072这听起来像AsNoTracking()绝对是首选的方法。 – emragins

2

更新:这并没有真正的工作。看评论!

我讨厌它,当我在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(); 
    } 

它使用反射,这意味着有人会很快评论这是一个性能问题。但是,这真的是一个打击?取决于你的用例。

+3

我还没有测试过,但据我所知,'AsNoTracking()'只返回当前设置为未跟踪的'IQueryable'。在'DbSet'上调用它不会使下一个查询未被跟踪。我从这段代码中得到的理解是,你在所有的集合上都调用了'AsNoTracking()',但是除非你使用返回的可查询任何东西 – Jcl

+0

@Jcl,否则它不会做任何事情,我必须检查它。它似乎在为我工作。 – Rhyous

+1

我现在没有时间来测试它,但乍一看对我来说很可疑。如果这样做的话,这意味着只要你在上下文中的'DbSet'上调用'AsNoTracking()',那么在同一个上下文中'DbSet'上的所有后续查询都将不被跟踪......而那个是一些奇怪的行为(特别是考虑到没有'AsTracking()'来补偿)。如果这确实有效,我会说这是一个错误...或无证的功能:-) – Jcl

0

在我来说,因为我需要的整个来龙去脉是只读的,而不是读/写。

所以我做了tt文件的更改,并更改了所有DbContext属性以返回DbQuery而不是DbSet,从所有属性中删除了这些集合,并且为get而返回了Model。AsNoTracking()

例如:

public virtual DbQuery<Campaign> Campaigns { get{ return Set<Campaign>().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)); 
 
    }