2012-05-15 23 views
5

He estado utilizando Entity Framework con el primer enfoque de POCO. He seguido bastante el patrón descrito por Steve Sanderson en su libro 'Pro ASP.NET MVC 3 Framework', usando un contenedor DI y una clase DbContext para conectarme a SQL Server.Mejorando la eficiencia con Entity Framework

Las tablas subyacentes en el servidor SQL contienen conjuntos de datos muy grandes utilizados por diferentes aplicaciones. Debido a esto he tenido que crear vistas para las entidades que necesito en mi solicitud:

class RemoteServerContext : DbContext 
{ 
    public DbSet<Customer> Customers { get; set; } 
    public DbSet<Order> Orders { get; set; } 
    public DbSet<Contact> Contacts { get; set; } 
    ... 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<Customer>().ToTable("vw_Customers"); 
     modelBuilder.Entity<Order>().ToTable("vw_Orders"); 
     ... 
    } 
} 

y esto parece que funciona bien para la mayoría de mis necesidades.

El problema que tengo es que algunos de estos puntos de vista tienen una gran cantidad de datos en ellos para que cuando llamo algo como:

var customers = _repository.Customers().Where(c => c.Location == location).Where(...); 

parece que está trayendo de vuelta todo el conjunto de datos, lo que puede tómese un tiempo antes de que la consulta LINQ reduzca el conjunto a los que necesito. Esto parece muy ineficiente cuando el criterio solo es aplicable a unos pocos registros y recupero todo el conjunto de datos del servidor SQL.

me han tratado de evitar esto mediante el uso de procedimientos almacenados, tales como

public IEnumerable<Customer> CustomersThatMatchACriteria(string criteria1, string criteria2, ...) //or an object passed in! 
{ 
    return Database.SqlQuery<Customer>("Exec pp_GetCustomersForCriteria @crit1 = {0}, @crit2 = {1}...", criteria1, criteria2,...); 
} 

mientras que esto es mucho más rápido, el problema aquí es que no devuelve un DbSet y por lo que pierde toda la conectividad entre mis objetos, por ejemplo No puedo hacer referencia a ningún objeto asociado, como pedidos o contactos, incluso si incluyo sus ID porque el tipo de devolución es una colección de 'Clientes' en lugar de un DbSet de ellos.

¿Alguien tiene una forma mejor de hacer que SQL Server haga las consultas para que no esté pasando mucha información no utilizada?

Respuesta

4
var customers = _repository.Customers().Where(c => c.Location == location).Where(... 

Si Customers() vuelve IQueryable, esta declaración por sí sola no será realmente 'trayendo de vuelta' nada en absoluto - que llamar Where en una IQueryable da otra IQueryable, y no es hasta que haga algo que causa la ejecución de consultas (como ToList o FirstOrDefault) que todo se ejecutará realmente y se devolverán los resultados.

Sin embargo, si este método Customers devuelve una colección de objetos instanciados, entonces sí, ya que está solicitando todos los objetos que los está obteniendo todos.

Nunca utilicé el primer código ni incluso el patrón de repositorio, así que no sé qué aconsejar, salvo permanecer en el dominio de IQueryable durante el mayor tiempo posible y solo ejecutar la consulta una vez has aplicado todos los filtros relevantes.

+0

+1. Para obtener más 'enfoque extensible' puede escribir una función que toma un predicado y devuelve ya sea '_repository.Customers(). Where (predicate)' o (si ya no es IQueryable) escriba una función separada con 'context.CreateQuery ("Clientes"). Donde (predicado) ', con posibilidad de invocar' .ToList() 'al final. Debería construir una expresión agradable y optimizada. –

+1

Hola. Has notado tu sugerencia de mantenerte en el reino de IQueryable. Estaba usando un IEnumerable que no pasa la consulta al servidor pero obtiene todos los registros y luego los filtra. Vea el artículo aquí: http://www.fascinatedwithsoftware.com/blog/post/2011/06/27/IEnumerable-IQueryable-and-the-Entity-Framework-40.aspx Lo he consultado con SQL Profiler y él tiene razón, IQueryable pasa los parámetros como una consulta – GrahamJRoy

0

Necesita la consulta LINQ para devolver menos datos como paginación sql como top función en sql o hacer consultas manuales utilizando procedimientos almacenados. En cualquier caso, debe volver a escribir su mecanismo de consulta. Esta es una de las razones por las que no usé EF, porque parece que no tienes mucho control sobre el código.

2

Lo que yo he hecho para volver sólo un conjunto de datos se haya acordado lo siguiente:

var customers = (from x in Repository.Customers where <boolean statement> &&/|| <boolean statement select new {variableName = x.Name , ...).Take(<integer amount for amount of records you need>); 

así por ejemplo:

var customers = (from x in _repository.Customers where x.ID == id select new {variableName = x.Name}).take(1000); 

continuación, iterar a través de los resultados para obtener los datos: (recuerde, la instrucción linq devuelve un IQueryable) ...

foreach (var data in customers) 
{ 
    string doSomething = data.variableName; //to get data from your query. 
} 

Espero que esto ayude, no exactamente el mismos métodos, pero esto me parece útil en mi código

1

Probablemente es porque su método Cusomters() en su repositorio está haciendo un tipo de cosas GetAll() y busca primero la lista completa. Esto prohíbe que LINQ y su SQL Server creen consultas inteligentes.

No sé si hay una buena solución para su repositorio, pero si quiere hacer algo como:

using(var db = new RemoteServerContext()) 
{ 
    var custs = db.Customers.Where(...); 
} 

Creo que va a ser mucho más rápido. Si su proyecto es lo suficientemente pequeño, puede hacerlo sin un repositorio. Claro, perderás una capa de abstracción, pero con pequeños proyectos esto puede no ser un gran problema.

Por otro lado, puede cargar todos los clientes en su repositorio una vez y usar la colección resultante directamente (en lugar de la llamada de método que llena la lista). Sin embargo, tenga cuidado de agregar, eliminar y modificar a los Clientes.