Compartir a través de


Seguimiento en comparación con consultas de No-Tracking

El control del comportamiento de seguimiento determina si Entity Framework Core mantiene información sobre una instancia de entidad en su rastreador de cambios. Si se realiza un seguimiento de una entidad, los cambios detectados en la entidad se conservan en la base de datos durante SaveChanges. EF Core también actualiza las propiedades de navegación entre las entidades en el resultado de una consulta rastreada y las entidades que están en el rastreador de cambios.

Nota:

Nunca se realiza un seguimiento de los tipos de entidad sin claves. Siempre que este artículo mencione los tipos de entidad, hace referencia a los tipos de entidad que tienen una clave definida.

Sugerencia

Puede ver un ejemplo de este artículo en GitHub.

Seguimiento de consultas

De forma predeterminada, las consultas que devuelven tipos de entidad realizan un seguimiento. Una consulta de seguimiento significa que los cambios en las instancias de entidad se conservan mediante SaveChanges. En el ejemplo siguiente, se detecta el cambio en la clasificación de blogs y se conserva en la base de datos durante SaveChanges:

var blog = await context.Blogs.SingleOrDefaultAsync(b => b.BlogId == 1);
blog.Rating = 5;
await context.SaveChangesAsync();

Cuando se devuelven los resultados en una consulta de seguimiento, EF Core comprueba si la entidad ya está en el contexto. Si EF Core encuentra una entidad existente, se devuelve la misma instancia, que puede usar menos memoria y ser más rápida que una consulta sin seguimiento. EF Core no sobrescribe los valores actuales y originales de las propiedades de la entidad en la entrada con los valores de la base de datos. Si la entidad no se encuentra en el contexto, EF Core crea una nueva instancia de entidad y la asocia al contexto. Los resultados de la consulta no contienen ninguna entidad que se agregue al contexto, pero que aún no se guarde en la base de datos.

Consultas sin seguimiento

Las consultas sin seguimiento son útiles cuando los resultados se usan en un escenario de solo lectura. Por lo general, son más rápidos de ejecutar porque no es necesario configurar la información de seguimiento de cambios. Si no es necesario actualizar las entidades recuperadas de la base de datos, se debe usar una consulta sin seguimiento. Se puede establecer una consulta individual para que no sea de seguimiento. Una consulta sin seguimiento también proporciona resultados en función de lo que se encuentra en la base de datos sin tener en cuenta los cambios locales o las entidades agregadas.

var blogs = await context.Blogs
    .AsNoTracking()
    .ToListAsync();

El comportamiento de seguimiento predeterminado se puede cambiar en el nivel de instancia de contexto:

context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

var blogs = await context.Blogs.ToListAsync();

En la sección siguiente se explica cuándo una consulta sin seguimiento podría ser menos eficaz que una consulta de seguimiento.

Resolución de identidad

Dado que una consulta de seguimiento usa el seguimiento de cambios, EF Core realiza la resolución de identidad en dicha consulta. Al materializar una entidad, EF Core devuelve la misma instancia de entidad del seguimiento de cambios si ya está siendo rastreada. Si el resultado contiene la misma entidad varias veces, se devuelve la misma instancia para cada repetición. Consultas sin seguimiento:

  • No use el rastreador de cambios y no realice la resolución de identidades.
  • Devuelve una nueva instancia de la entidad incluso cuando la misma entidad está contenida en el resultado varias veces.

El seguimiento y el no seguimiento se pueden combinar en la misma consulta. Es decir, puede tener una consulta sin seguimiento, que realiza la resolución de identidad en los resultados. Al igual que AsNoTracking el operador consultable, hemos agregado otro operador AsNoTrackingWithIdentityResolution<TEntity>(IQueryable<TEntity>). También hay una entrada asociada agregada en la QueryTrackingBehavior enumeración. Cuando la consulta para usar la resolución de identidades está configurada sin seguimiento, se usa un seguimiento de cambios independiente en segundo plano al generar resultados de consulta para que cada instancia se materialice solo una vez. Dado que este rastreador de cambios es diferente del que se encuentra en el contexto, el contexto no realiza un seguimiento de los resultados. Una vez que la consulta se enumera por completo, el rastreador de cambios sale del alcance y es recolectado por el recolector de basura según sea necesario.

var blogs = await context.Blogs
    .AsNoTrackingWithIdentityResolution()
    .ToListAsync();

Configuración del comportamiento de seguimiento predeterminado

Si se encuentra cambiando el comportamiento de seguimiento de muchas consultas, puede que desee cambiar el valor predeterminado en su lugar:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying.Tracking;Trusted_Connection=True;ConnectRetryCount=0")
        .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}

Esto hace que todas las consultas sean sin rastreo por defecto. Todavía puede agregar AsTracking para realizar un seguimiento de consultas específicas.

Seguimiento y proyecciones personalizadas

Aunque el tipo de resultado de la consulta no sea un tipo de entidad, EF Core seguirá realizando un seguimiento de los tipos de entidad incluidos en el resultado de forma predeterminada. En la consulta siguiente, que devuelve un tipo anónimo, se realizará un seguimiento de las instancias de Blog en el conjunto de resultados.

var blog = context.Blogs
    .Select(
        b =>
            new { Blog = b, PostCount = b.Posts.Count() });

Si el conjunto de resultados contiene tipos de entidad procedentes de la composición de LINQ, EF Core los realizará un seguimiento.

var blog = context.Blogs
    .Select(
        b =>
            new { Blog = b, Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault() });

Si el conjunto de resultados no contiene ningún tipo de entidad, no se realiza ningún seguimiento. En la consulta siguiente, se devuelve un tipo anónimo con algunos de los valores de la entidad (pero no hay instancias del tipo de entidad real). No hay entidades rastreadas que salgan de la consulta.

var blog = context.Blogs
    .Select(
        b =>
            new { Id = b.BlogId, b.Url });

EF Core admite la evaluación de cliente en la proyección de nivel superior. Si EF Core materializa una instancia de entidad para la evaluación de cliente, se realizará el seguimiento. Aquí, dado que estamos pasando blog entidades al método del cliente StandardizeURL, EF Core también realizará un seguimiento de las instancias de blog.

var blogs = await context.Blogs
    .OrderByDescending(blog => blog.Rating)
    .Select(
        blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
    .ToListAsync();
public static string StandardizeUrl(Blog blog)
{
    var url = blog.Url.ToLower();

    if (!url.StartsWith("http://"))
    {
        url = string.Concat("http://", url);
    }

    return url;
}

EF Core no realiza un seguimiento de las instancias de entidad sin claves contenidas en el resultado. Pero EF Core realiza un seguimiento de todas las demás instancias de tipos de entidad con una clave según las reglas anteriores.

Versiones anteriores

Antes de la versión 3.0, EF Core tenía algunas diferencias en cómo se hacía el seguimiento. Las diferencias importantes son las siguientes:

  • Como se explica en la página Evaluación de cliente frente a servidor , EF Core admite la evaluación de cliente en cualquier parte de la consulta antes de la versión 3.0. La evaluación del cliente provocó la materialización de entidades, que no formaban parte del resultado. Por lo tanto, EF Core analizó el resultado para detectar qué realizar un seguimiento. Este diseño tenía ciertas diferencias como se indica a continuación:

    • La evaluación del cliente en la proyección, lo que provocó la materialización, pero no devolvió la instancia de entidad materializada no se ha seguido. En el ejemplo siguiente no se realiza un seguimiento de las entidades blog.

      var blogs = await context.Blogs
          .OrderByDescending(blog => blog.Rating)
          .Select(
              blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog) })
          .ToListAsync();
      
    • EF Core no siguió los objetos procedentes de la composición LINQ en determinados casos. En el siguiente ejemplo no se ha rastreado Post.

      var blog = context.Blogs
          .Select(
              b =>
                  new { Blog = b, Post = b.Posts.OrderBy(p => p.Rating).LastOrDefault() });
      
  • Cada vez que los resultados de la consulta contenían tipos de entidad sin clave, toda la consulta se realizó sin seguimiento. Esto significa que los tipos de entidad con claves que están en el resultado tampoco estaban siendo rastreados.

  • EF Core se usa para realizar la resolución de identidades en consultas sin seguimiento. Usó referencias débiles para realizar un seguimiento de las entidades que ya se habían devuelto. Por lo tanto, si un conjunto de resultados contenía la misma entidad varias veces, obtendría la misma instancia para cada repetición. Si un resultado anterior con la misma identidad salió del ámbito y fue reciclado por el recolector de basura, EF Core devolvió una nueva instancia.