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

I have the following code in aync method:

using System.Data.Entity;

// ...

protected internal async Task<Customer> GetCustomerAsync(int entityId, CancellationToken cancellationToken)
{
    IQueryable<Customer> query = (await this.Repository.GetAll(cancellationToken)).Where(e => e.Id == entityId);

    query = query.Include(e => e.Partners);

    string sql = query.ToString();

    return await query.AsNotTracking().FirstOrDefaultAsync(cancellationToken);
}

The sql variable contains, something like this:

SELECT 
[Project1].[UserId] AS [UserId], 
[Project1].[EntityId] AS [EntityId], 
[Project1].[Id] AS [Id], 
[Project1].[OtherColumn] AS [OtherColumn], 
[Project1].[Name] AS [Name],
[Project1].[C1] AS [C1]
FROM ( SELECT 
    [Extent1].[UserId] AS [UserId], 
    [Extent1].[EntityId] AS [EntityId], 
    [Extent2].[Id] AS [Id], 
    [Extent2].[OtherColumn] AS [OtherColumn]
    [Extent3].[Id] AS [Id1], 
    [Extent3].[CustomerId] AS [CustomerId], 
    [Extent3].[PartnerId] AS [PartnerId], 
    CASE WHEN ([Extent3].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
    FROM   [op].[CustomerAcls] AS [Extent1]
    INNER JOIN [op].[Customers] AS [Extent2] ON [Extent1].[EntityId] = [Extent2].[Id]
    LEFT OUTER JOIN [op].[CustomerPartners] AS [Extent3] ON [Extent2].[Id] = [Extent3].[CustomerId]
    WHERE ([Extent1].[UserId] = @p__linq__0) AND ([Extent1].[AccessRight] >= @p__linq__1) AND ([Extent2].[Id] = @p__linq__2)
)  AS [Project1]
ORDER BY [Project1].[UserId] ASC, [Project1].[EntityId] ASC, [Project1].[Id] ASC, [Project1].[C1] ASC

UPDATE -----

The code of the repository is like the following:

public class CustomerRepository<TAcl> : ICustomerRepository
    where TAcl : class, IAcl<Customer>, new()
{
    private readonly DbSet<TAcl> aclSet;

    public CustomerRepository(
        BranchDbContext branchDbContext,
        Func<BranchDbContext, DbSet<TAcl>> aclSetFunc)
    {   
        this.aclSet = aclSetFunc(branchDbContext);
    }

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

------------

Despite of the use of the AsNoTracking() method (see return statement), and even though that I made sure the repository gives me back a LinqToEntities query (it's not already materialized), the resulting returned instance (of the method GetCustomerAsync) has still lazy loading capabilities.

The documentation is so vague, it says:

If the underlying query object does not have a AsNoTracking method, then calling this method will have no affect.

Does someone see where the problem is in my code or understand what the documentation says?

THX



Solution 1:[1]

How did you check that the entity is not tracked. The AsNoTracking method's purpose is to exclude the fetched records from entity framework change tracker (for example when you are using the data only for read purposes and want to prevent accidentally persisting data in database).

See AsNoTracking documentation https://docs.microsoft.com/en-us/ef/core/querying/tracking

I suggest to check DbContext (after you run the query) and check whether the entity you fetched from database is being tracked in change tracker.

Also consider using FindAsync instead of FirstOrDefaultAsync in case that the entityId is primary key of the Customer table.

This is how I would approach writing the method:

protected internal async Task<Customer> GetCustomerAsync(int entityId, CancellationToken cancellationToken)
{
    return this.Repository
        .AsNoTracking()
        .Include(e => e.Partners)
        .FindAsync(entityId, cancellationToken);
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Sonyck