Global Setting for Asnotracking()

Global setting for AsNoTracking()?

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

Where should AsNoTracking be applied?

In the first scenario, you can simply apply AsNoTracking to the result of the query:

public IQueryable<A> Get() {
var query =
from a in db.As
join b in db.Bs
on a.P1 equals b.P3
where b.P4 > 50
select a;

return query.AsNoTracking();
}

In the second scenario, since you are returning something not in the DbContext then its not tracked anyway so you don't need to do anything.

You can inspect the change tracker after querying to see what's being tracked and what's not.

var tc = db.ChangeTracker.Entries().Count();

EF's AsNoTracking() doesn't prevent the resulting entities to be tracked (+ still have lazy loading on them)

This is not necessarily indicative that the entities are tracked as a result of that query. EF 6 will also still lazy load referenced entities in non-tracked results provided lazy loading is enabled and the navigation properties are virtual.

As a simple example with a Parent and Children collection:

EF 6:

using (var context = new TestDbContext())
{
var parent = context.Parents.AsNoTracking().Single(x => x.ParentId == 1);
var count = parent.Children.Count;
}

Here with lazy loading enabled and the Children declared as a virtual ICollection<Child>, EF 6 will trigger a lazy load call when parent.Children is accessed. This can be verified by running a profiler against your DB with a breakpoint and you will see the lazy load call go out.

To avoid this you would need to turn Lazy Loading off, or ensure the Children property is not declared as virtual. In either of those cases, "count" would come back as 0 (or trigger a null ref if not initialized to an empty list)

With EF core things are a bit different. By default with EF Core, Lazy Loading is disabled so "count" would always come back as 0. If you enable lazy loading proxies in your DbContext then the above code would result in an InvalidOperationException as now an AsNoTracking() entity is properly detected as Detached and throws this exception if you attempt to lazy load from a detached instance. (EF6 allowed this if the DbContext was still in scope)

Both EF6 and EF Core will not pre-populate related entities that might already be tracked when fetching a NoTracking() instance.

For example, provided lazy loading is disabled or non-virtual collection:

using (var context = new TestDbContext())
{
var parent = context.Parents.Include(x => x.Children).AsNoTracking().Single(x => x.ParentId == 1);
var count = parent.Children.Count; // eager load, will return count.
}

using (var context = new TestDbContext())
{
var parent = context.Parents.AsNoTracking().Single(x => x.ParentId == 1);
var count = parent.Children.Count; // 0.
}

using (var context = new TestDbContext())
{
var children = context.Children.Where(x => x.ParentId == 1).ToList();
var parent = context.Parents.Single(x => x.ParentId == 1);
var count = parent.Children.Count; // not eager or lazy loaded, will still return count.
// (This can be inaccurate if only *some* children were previously loaded.)
}

using (var context = new TestDbContext())
{
var children = context.Children.Where(x => x.ParentId == 1).ToList();
var parent = context.Parents.AsNoTracking().Single(x => x.ParentId == 1);
var count = parent.Children.Count; // will still return 0.
}

So if you are seeing something else unexpectedly and lazy loading is definitely disabled and confirmed not to be happening (no queries observed in a profiler) then I suspect you have something else at play.

This code for instance sends off red flags:

public async Task<IQueryable<Customer>> GetAll(CancellationToken cancellationToken)
{
int minAccessRight = //...
int currentUserId = //...

return from acl in (from acl in this.aclSet
where acl.UserId == currentUserId &&
acl.AccessRight >= minAccessRight
select acl)
select acl.Entity;
}

IQueryable returning methods do not need to be declared as async and possibly should not as this may be triggering a tracking-like query actually executing or some other odd behaviour. Instead, try:

public IQueryable<Customer> GetAll(CancellationToken cancellationToken)
{
int minAccessRight = //...
int currentUserId = //...

var query = (from acl in this.aclSet
where acl.UserId == currentUserId &&
acl.AccessRight >= minAccessRight
select acl);
return query;
}

Then in calling:

IQueryable<Customer> query = Repository.GetAll(cancellationToken)
.Include(e => e.Partners)
.Where(e => e.Id == entityId)
.AsNoTracking();

return await query.FirstOrDefaultAsync(cancellationToken);

The execution of the query is still asynchronous, and that is where the async matters. The only reason the query composition might need to be asynchronous is if in building that query resulted in some particularly expensive checks etc. that might benefit from being async, however I'd probably delegate that back out and have those values pre-fetched asynchronously and have the values passed in as parameters. (I.e. if getting minAccessRight/currentUserId etc. handed off to async calls, load these IDs and pass them in as parameters instead)

AsNoTracking() and Include

Use AsNoTracking after you have completed all your query parameters but before you move the data into memory. In this example, you'll want:

context.MyEntity
.Include(i=> i.Nav1)
.Include(i=> i.Nav2)
.Where(x=> x.Prop1==1)
.AsNoTracking()
.FirstOrDefault();

Any child objects of the parent entity will not be tracked.

Does it matter where AsNoTracking in Entity Framework is called

No it doesn't matter: (source)

A new query with NoTracking applied, or the source query if NoTracking is not supported.

So you either do it in the beginning and you expand the "new" query with the method chain, or you do it in the end and then get the "new" query. As long as you call it before the query is executed you're fine.

What difference does .AsNoTracking() make?

The difference is that in the first case the retrieved user is not tracked by the context so when you are going to save the user back to database you must attach it and set correctly state of the user so that EF knows that it should update existing user instead of inserting a new one. In the second case you don't need to do that if you load and save the user with the same context instance because the tracking mechanism handles that for you.

Entity Framework AsNoTracking() Infinite Navigation Property

No it's not important where to call AsNoTracking(). Basically all it does is sets ObjectQuery.MergeOption (and some other properties) on internal ObjectQuery, so it doesn't matter where to do that until actual query execution.

Yes, all Includeed entities will also not be tracked by a context.

No, LazyLoadingEnabled = false, ProxyCreationEnabled = false and AsNoTracking() will not solve all your infinite recursion problems, especially if you are using includes. Look for how to map such recursive entities with automapper correctly, doing anything on EF side will not help.

Entity Framework: difference between Detach and AsNoTracking

The first version is better and I would prefer it because

  • it expresses better that you don't want to track changes of the existing entity
  • it doesn't attach the entity to the context in the first place while the second version attaches and then immediately detaches it (which most likely will also have slightly worse performance)
  • it perserves relationships (doesn't matter in this simple example, but generally) while detaching an entity only detaches the entity itself that you pass into Detach. Related children will stay attached which comes with the price that the relationships will be cleared (a navigation collection of child entities for example would be emptied, a reference navigation property would be set to null) since EF does not allow object graphs with a mix of attached and detached entities.

AsNoTracking() Method Is Missing From Context in Entity Framework

AsNoTracking() is an extension method, which was added in Entity Framework 4.1 (as ability to return non-cached results). That's why you don't have it in Entity Framework 4.0. I suggest you to upgrade version of Entity Framework, if it is possible (BTW current version is 6.0).



Related Topics



Leave a reply



Submit