【发布时间】:2012-10-04 12:04:04
【问题描述】:
原来我是这么认为的
context.Configuration.AutoDetectChangesEnabled = false;
将禁用更改跟踪。但不是。目前,我需要在所有 LINQ 查询(对于我的只读层)上使用 AsNoTracking()。是否有全局设置来禁用 DbContext 上的跟踪?
【问题讨论】:
标签: c# .net entity-framework change-tracking
原来我是这么认为的
context.Configuration.AutoDetectChangesEnabled = false;
将禁用更改跟踪。但不是。目前,我需要在所有 LINQ 查询(对于我的只读层)上使用 AsNoTracking()。是否有全局设置来禁用 DbContext 上的跟踪?
【问题讨论】:
标签: c# .net entity-framework change-tracking
由于这个问题没有用特定的 EF 版本标记,我想提一下,在 EF Core 中,行为可以是 configured at the context level。
您还可以在上下文中更改默认跟踪行为 实例级别:
using (var context = new BloggingContext())
{
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var blogs = context.Blogs.ToList();
}
【讨论】:
var dbContextOptionsBuilder = new DbContextOptionsBuilder<BloggingContext>().UseSqlServer("SQL connection to Blogging database").UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);。然后使用var bloggingContext = new BloggingContext(dbContextOptionsBuilder.Options) 创建上下文。
如何在派生上下文中简单地公开这样的方法并将其用于查询:
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调用的结果
在 EntityFramework.Core 中非常简单。
为此,您可以使用UseQueryTrackingBehavior 方法。
代码 sn-p 在这里:
services.AddDbContext<DatabaseContext>(options =>
{
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
options.UseSqlServer(databaseSettings.DefaultConnection);
});
【讨论】:
你可以在你的 DbContext 中做这样的事情:
public void ObjectContext_OnObjectMaterialized(Object objSender, ObjectMaterializedEventArgs e)
{
Entry(e.Entity).State = EntityState.Detached;
}
每当一个对象被你的上下文具体化时,它就会被分离并且不再被跟踪。
【讨论】:
更新:这并没有真正奏效。见 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(),但除非您将返回的可查询对象用于任何内容,否则这不会做任何事情
DbSet 调用AsNoTracking(),在同一上下文中对该DbSet 的所有后续查询都将不被跟踪......奇怪的行为(特别考虑到没有AsTracking() 可以补偿)。如果这确实有效,我会说这是一个错误......或未记录的功能:-)
就我而言,因为我需要将整个上下文设为只读而不是读/写。
所以我对 tt 文件进行了更改,并将所有 DbContext 属性更改为返回 DbQuery 而不是 DbSet,从所有属性中删除了集合,对于获取,我返回了 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));
}
【讨论】:
如果使用实体框架核心,您还可以在继承 DbContext 的类的构造函数中添加以下代码。
public NPCContext()
: base()
{
base.ChangeTracker.AutoDetectChangesEnabled = false;
}
或以下
public NPCContext(DbContextOptions<NPCContext> options)
: base(options)
{
base.ChangeTracker.AutoDetectChangesEnabled = false;
}
【讨论】: