Buscar..


Usando AsNoTracking

Mal ejemplo:

var location =  dbContext.Location
                     .Where(l => l.Location.ID == location_ID)
                     .SingleOrDefault();

return location;

Dado que el código anterior simplemente devuelve una entidad sin modificarla ni agregarla, podemos evitar el seguimiento del costo.

Buen ejemplo:

var location =  dbContext.Location.AsNoTracking()
                     .Where(l => l.Location.ID == location_ID)
                     .SingleOrDefault();

return location;

Cuando usamos la función AsNoTracking() le estamos diciendo explícitamente a Entity Framework que el contexto no rastrea a las entidades. Esto puede ser especialmente útil cuando recupera grandes cantidades de datos de su almacén de datos. Sin embargo, si desea realizar cambios en las entidades sin seguimiento, debe recordar adjuntarlos antes de llamar a SaveChanges .

Cargando solo los datos requeridos

Un problema que se ve a menudo en el código es cargar todos los datos. Esto aumentará en gran medida la carga en el servidor.

Digamos que tengo un modelo llamado "ubicación" que tiene 10 campos, pero no todos los campos son obligatorios al mismo tiempo. Digamos que solo quiero el parámetro 'LocationName' de ese modelo.

Mal ejemplo

var location =  dbContext.Location.AsNoTracking()
                         .Where(l => l.Location.ID == location_ID)
                         .SingleOrDefault();

return location.Name;

Buen ejemplo

var location =  dbContext.Location
                          .Where(l => l.Location.ID == location_ID)
                          .Select(l => l.LocationName);
                          .SingleOrDefault();

return location;

El código en el "buen ejemplo" solo buscará 'LocationName' y nada más.

Tenga en cuenta que como no se materializa ninguna entidad en este ejemplo, AsNoTracking() no es necesario. No hay nada que rastrear de todos modos.

Obteniendo más campos con tipos anónimos

var location = dbContext.Location
                    .Where(l => l.Location.ID == location_ID)
                    .Select(l => new { Name = l.LocationName, Area = l.LocationArea })
                    .SingleOrDefault();

return location.Name + " has an area of " + location.Area;

Igual que en el ejemplo anterior, solo los campos 'LocationName' y 'LocationArea' se recuperarán de la base de datos, el Tipo Anónimo puede contener tantos valores que desee.

Ejecute las consultas en la base de datos cuando sea posible, no en la memoria.

Supongamos que queremos contar cuántos condados hay en Texas:

var counties = dbContext.States.Single(s => s.Code == "tx").Counties.Count();

La consulta es correcta, pero ineficiente. States.Single(…) carga un estado desde la base de datos. A continuación, los Counties cargan los 254 condados con todos sus campos en una segunda consulta. .Count() luego se realiza en memoria en la colección de Counties cargados.
Hemos cargado una gran cantidad de datos que no necesitamos, y podemos hacerlo mejor:

var counties = dbContext.Counties.Count(c => c.State.Code == "tx");

Aquí solo hacemos una consulta, que en SQL se traduce en un recuento y una combinación. Solo devolvemos el recuento de la base de datos; hemos guardado filas, campos y creación de objetos.

Es fácil ver dónde se realiza la consulta mirando el tipo de colección: IQueryable<T> frente a IEnumerable<T> .

Ejecutar múltiples consultas asíncronas y en paralelo.

Al utilizar consultas asíncronas, puede ejecutar varias consultas al mismo tiempo, pero no en el mismo contexto. Si el tiempo de ejecución de una consulta es 10s, el tiempo para el mal ejemplo será 20s, mientras que el tiempo para el buen ejemplo será 10s.

Mal ejemplo

IEnumerable<TResult1> result1;
IEnumerable<TResult2> result2;

using(var context = new Context())
{
    result1 = await context.Set<TResult1>().ToListAsync().ConfigureAwait(false);
    result2 = await context.Set<TResult1>().ToListAsync().ConfigureAwait(false);
}

Buen ejemplo

public async Task<IEnumerable<TResult>> GetResult<TResult>()
{
    using(var context = new Context())
    {
        return await context.Set<TResult1>().ToListAsync().ConfigureAwait(false);
    }
}



IEnumerable<TResult1> result1;
IEnumerable<TResult2> result2;

var result1Task = GetResult<TResult1>();
var result2Task = GetResult<TResult2>();

await Task.WhenAll(result1Task, result2Task).ConfigureAwait(false);

var result1 = result1Task.Result;
var result2 = result2Task.Result;

Deshabilitar el seguimiento de cambios y la generación de proxy

Si solo desea obtener datos, pero no modificar nada, puede desactivar el seguimiento de cambios y la creación de proxy. Esto mejorará su rendimiento y también evitará la carga perezosa.

Mal ejemplo:

using(var context = new Context())
{
    return await context.Set<MyEntity>().ToListAsync().ConfigureAwait(false);
}

Buen ejemplo:

using(var context = new Context())
{
    context.Configuration.AutoDetectChangesEnabled = false;
    context.Configuration.ProxyCreationEnabled = false;

    return await context.Set<MyEntity>().ToListAsync().ConfigureAwait(false);
}

Es particularmente común desactivar esto dentro del constructor de su contexto, especialmente si desea que estos se configuren en su solución:

public class MyContext : DbContext
{
    public MyContext()
        : base("MyContext")
    {
        Configuration.AutoDetectChangesEnabled = false;
        Configuration.ProxyCreationEnabled = false;
    }

    //snip
}

Trabajando con entidades de stub

Digamos que tenemos Product y Category en una relación de muchos a muchos:

public class Product
{
    public Product()
    {
        Categories = new HashSet<Category>();
    }
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public virtual ICollection<Category> Categories { get; private set; }
}

public class Category
{
    public Category()
    {
        Products = new HashSet<Product>();
    }
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public virtual ICollection<Product> Products { get; set; }
}

Si queremos agregar una Category a un Product , tenemos que cargar el producto y agregar la categoría a sus Categories , por ejemplo:

Mal ejemplo:

var product = db.Products.Find(1);
var category = db.Categories.Find(2);
product.Categories.Add(category);
db.SaveChanges();

(donde db es una subclase DbContext ).

Esto crea un registro en la tabla de unión entre Product y Category . Sin embargo, esta tabla solo contiene dos valores de Id . Es una pérdida de recursos cargar dos entidades completas para crear un registro pequeño.

Una forma más eficiente es usar entidades de código auxiliar , es decir, objetos de entidad, creados en la memoria, que contienen solo el mínimo de datos, generalmente solo un valor de Id . Esto es lo que parece:

Buen ejemplo:

// Create two stub entities
var product = new Product { ProductId = 1 };
var category = new Category { CategoryId = 2 };

// Attach the stub entities to the context
db.Entry(product).State = System.Data.Entity.EntityState.Unchanged;
db.Entry(category).State = System.Data.Entity.EntityState.Unchanged;

product.Categories.Add(category);
db.SaveChanges();

El resultado final es el mismo, pero evita dos viajes de ida y vuelta a la base de datos.

Prevenir duplicados

Si desea comprobar si la asociación ya existe, una consulta barata es suficiente. Por ejemplo:

var exists = db.Categories.Any(c => c.Id == 1 && c.Products.Any(p => p.Id == 14));

De nuevo, esto no cargará entidades completas en la memoria. Consulta eficazmente la tabla de unión y solo devuelve un valor booleano.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow