40

Does it matter where the AsNoTracking method is called when writing an Entity Framework query? e.g.

var matchingCustomers = context.Customers.AsNoTracking().Where(n => n.city == "Milan").Skip(50).Take(100).OrderBy(n => n.Name).ToList();
var matchingCustomers = context.Customers.Where(n => n.city == "Milan").AsNoTracking().Skip(50).Take(100).OrderBy(n => n.Name).ToList();
var matchingCustomers = context.Customers.Where(n => n.city == "Milan").Skip(50).AsNoTracking().Take(100).OrderBy(n => n.Name).ToList();
var matchingCustomers = context.Customers.Where(n => n.city == "Milan").Skip(50).Take(100).AsNoTracking().OrderBy(n => n.Name).ToList();
var matchingCustomers = context.Customers.Where(n => n.city == "Milan").Skip(50).Take(100).OrderBy(n => n.Name).AsNoTracking().ToList();
var matchingCustomers = context.Customers.Where(n => n.city == "Milan").Skip(50).Take(100).OrderBy(n => n.Name).ToList().AsNoTracking();

I like adding it to the end of statements but before the ToList is called like this:

var matchingCustomers = context.Customers.Where(n => n.city == "Milan").Skip(50).Take(100).OrderBy(n => n.Name).AsNoTracking().ToList();
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
user1786107
  • 2,921
  • 5
  • 24
  • 35

2 Answers2

56

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.

Anttu
  • 1,076
  • 1
  • 10
  • 21
Alexander Derck
  • 13,818
  • 5
  • 54
  • 76
  • 3
    Actually it does in the last case - calling `AsNoTracking` after `ToList` will probably throw. If it doesn't, it will actually be a no-op as the entities already be in memory by the time `AsNoTracking` is called – Panagiotis Kanavos Feb 22 '16 at 12:52
  • 17
    @PanagiotisKanavos Yes, that's why I say "before the query is executed'. `ToList()` executes the query. I've put it in bold now – Alexander Derck Feb 22 '16 at 12:55
  • 1
    Link is dead, suppose this is the one? https://learn.microsoft.com/en-us/dotnet/api/system.data.entity.queryableextensions.asnotracking – Anttu Feb 10 '20 at 08:19
  • @PanagiotisKanavos It won't be a no-op. It will be a compile time error. You cannot call AsNoTracking after ToList because you no longer have an IQueryable to call it on. – Cdaragorn Oct 29 '20 at 19:00
  • @Cdaragorn List implements IEnumerable which implements IQueryable – Alexander Derck Oct 30 '20 at 17:55
  • 1
    @AlexanderDerck You've got your implementations reversed. IQueryable implements IEnumberable. – Cdaragorn Feb 22 '21 at 19:08
6

As Cdaragorn said in the comments.

You cannot call AsNoTracking after ToList because you no longer have an IQueryable to call it on. It will give a compile time error.

In the case you could do what OP is asking I am going to explain how the query would work because could help others to understand these matters:

With

var matchingCustomers = context.Customers.Where(n => n.city == "Milan").Skip(50).Take(100).OrderBy(n => n.Name).ToList().AsNoTracking();

you are trying to apply NoTracking to a data structure already in memory once EF has executed, and traked, the query.

When you use this fluent API; you are defining a query without executing it until you, of course, execute the query. ToList() will execute the query an bring the data to memory to transform it into a List<T> data structure.

Let's split the command to understand this:

  • context.Customers --> Select [*] from Customers
  • Where(n => n.city == "Milan") --> Select [*] from Customers where city == 'Milan'
  • Skip(50).Take(100) --> Select [*] from Customers where city == 'Milan' OFFSET 50 ROWS FETCH NEXT 100 ROWS ONLY
  • OrderBy name --> Select [*] from Customers where city == 'Milan' OFFSET 50 ROWS FETCH NEXT 100 ROWS ONLY ORDER BY name
  • ToList() --> Execute the query and bring the data into memory with Tracking by default!
  • AsNoTraking() --> Does nothing because EF already executed the query and tracked the data.
jlvaquero
  • 8,571
  • 1
  • 29
  • 45
  • 2
    Before answering a question please go try to do what you're going to talk about. You cannot call AsNoTracking after ToList because you no longer have an IQueryable to call it on. It will give a compile time error. – Cdaragorn Oct 29 '20 at 19:05
  • 4
    Physically impossible to compile is not "does nothing". – Cdaragorn Feb 22 '21 at 19:12