Global setting for AsNoTracking()?

狂风中的少年 提交于 2019-11-26 07:36:20

问题


Originally I believed that

context.Configuration.AutoDetectChangesEnabled = false;

would disable change tracking. But no. Currently I need to use AsNoTracking() on all my LINQ queries (for my read only layer). Is there a global setting to disable tracking on the DbContext?


回答1:


Since this question is not tagged with a specific EF version, I wanted to mention that in EF Core the behavior can be configured at the context level.

You can also change the default tracking behavior at the context instance level:

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

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



回答2:


What about simply exposing method like this on your derived context and use it for queries:

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

Setting AsNoTracking globally is not possible. You must set it per each query or per each ObjectSet (not DbSet). The latter approach requires using ObjectContext API.

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



回答3:


You could do something like this in your DbContext:

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

Every time an object is materialized by your context, it will be detached and no longer tracked.




回答4:


Update: This didn't really work. See comments!

I hate it when I search on StackOverflow and the answer is: "You can't!" or "You could, but only if you completely change every single call you've ever made."

Reflection anyone? I was hoping this would be a DbContext setting. But since it is not, I made one using reflection.

This handy little method will set AsNoTracking on all properties of type DbSet.

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

Add it to an overloaded DbContext constructor.

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

It uses reflection, which means someone will quickly comment that this is a performance hit. But is it really that much of a hit? Depends on your use case.




回答5:


In my case since I needed the whole context to be readonly rather than Read/Write.

So I did a change to the tt file, and changed all the DbContext properties to return DbQuery instead of DbSet, removed the sets from all properties, and for the gets, I returned the Model.AsNoTracking()

For example:

public virtual DbQuery<Campaign> Campaigns { get{ return Set<Campaign>().AsNoTracking();} }

The way I did this in the tt template is:

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


来源:https://stackoverflow.com/questions/12726878/global-setting-for-asnotracking

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!