76

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?

Alexander I.
  • 2,380
  • 3
  • 17
  • 42
Vindberg
  • 1,502
  • 2
  • 15
  • 27

8 Answers8

43

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();
}
Amr Elgarhy
  • 66,568
  • 69
  • 184
  • 301
  • 2
    If you have additional settings (e.g. connection string), then the DbContextOptions helps. For example: `var dbContextOptionsBuilder = new DbContextOptionsBuilder().UseSqlServer("SQL connection to Blogging database").UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);`. Then create context with `var bloggingContext = new BloggingContext(dbContextOptionsBuilder.Options)`. – Jeremy Ray Brown Dec 16 '20 at 13:25
35

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
coding Bott
  • 4,287
  • 1
  • 27
  • 44
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • 2
    How would do a join between Entities when only exposing a single entity like GetQuery? Thanks for the reply though. – Vindberg Oct 04 '12 at 12:50
  • you can join results of two different `GetQuery` calls – Ladislav Mrnka Oct 04 '12 at 16:25
  • Its possible, but then I need to redo my generic repository setup :/ But thanks for the suggestion. – Vindberg Oct 05 '12 at 09:08
  • @LadislavMrnka I have queries that return instances of a class not captured by the DbContext. In this case, I don't think this.Set would work. On the other hand, maybe AsNoTracking is not necessary? – Candy Chiu Oct 22 '14 at 13:03
32

In EntityFramework.Core it is very easy.

For this purpose you can use UseQueryTrackingBehavior method.

Code snippet is here:

services.AddDbContext<DatabaseContext>(options =>
{
    options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
    options.UseSqlServer(databaseSettings.DefaultConnection);
});
Alexander I.
  • 2,380
  • 3
  • 17
  • 42
1

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.

Gabriel G. Roy
  • 2,552
  • 2
  • 27
  • 39
  • I think this works but maybe not the best way to do this. AsNoTracking() as far as I know do not Attach and Detach objects. – Jone Polvora Apr 02 '15 at 12:24
  • 1
    The answer here elaborates on the difference between the two. http://stackoverflow.com/a/20163424/219072 It sounds like AsNoTracking() is definitely the preferred approach. – emragins Oct 26 '15 at 22:34
1

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

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.

Rhyous
  • 6,510
  • 2
  • 44
  • 50
  • 4
    I haven't tested this, but as far as I know, `AsNoTracking()` only returns the current set as a not-tracked `IQueryable`. Calling it on the `DbSet` doesn't make the next queries non-tracked. What I understand from this code is that you are calling `AsNoTracking()` on all your sets, but that doesn't do anything unless you are using the returned queryables for anything – Jcl Dec 06 '15 at 17:50
  • @Jcl, I'll have to check on that. It appears to be working for me. – Rhyous Dec 07 '15 at 17:08
  • 2
    I don't have time right now to test it, but it looks dubious to me at first glance. If this works, it'd mean that anytime you call `AsNoTracking()` on a `DbSet` in a context, all subsequent queries on that `DbSet` in the same context would be non-tracked... and that'd be some weird behaviour (specially considering there's no `AsTracking()` to compensate). If this indeed works, I'd say it's a bug... or undocumented feature :-) – Jcl Dec 07 '15 at 20:28
  • 1
    @Rhyous I dont think setting AsNoTracking() once will set it forever, as per this link. http://www.c-sharpcorner.com/UploadFile/ff2f08/entity-framework-and-asnotracking/ I see this is almost an year old comment.. please let me know know if you have any alternative for setting Globally AsNoTracking – Raghav Aug 08 '16 at 13:31
  • Forgive me for forgetting to post my findings. As @jcl stated, it is per query. A new query does not honor the AsNoTracking() command. I implemented the a wrapper around EF (the Repository pattern) and now the calls that need AsNoTracking have it. – Rhyous Aug 08 '16 at 14:55
1

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));
    }
Ahmed IG
  • 563
  • 7
  • 17
0

You can use the below code snippet as a reference

optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);

Here note that, Before EF Core 5.0, there was no way to enable identity resolution with “No Tracking”. But now, with EF Core 5.0, an additional API can be used to enable both “No Tracking” and identity resolution. So if you want to "No Tracking" with identity resolution then in EF Core 5.0 use the below snippet

optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTrackingWithIdentityResolution);
Achyut
  • 29
  • 8
-2

If using Entity Framework core you could also add the following code in the constructor of the class that is inheriting DbContext.

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

or the following

    public NPCContext(DbContextOptions<NPCContext> options)
        : base(options)
    {
        base.ChangeTracker.AutoDetectChangesEnabled = false;
    }
George M
  • 63
  • 2
  • 5