2012-03-12 30 views
5

Tengo el siguiente LINQ a Entidades consulta que tiene muchos sub consultas para obtener algunos datos agregados:Refactor LINQ a SQL/Entidades Consulte con muchas subconsultas

var systems = from s in db.Systems 
       orderby s.Name 
       select new SystemSummary 
       { 
        Id = s.Id, 
        Code = s.Code, 
        Name = s.Name, 
        LastException = (
         from a in s.Applications 
         from e in a.Summaries 
         select e.CreationDate 
       ).Max(), 
        TodaysExceptions = (
         from a in s.Applications 
         from e in a.Summaries 
         where e.CreationDate >= today && e.CreationDate < tomorrow 
         select e 
       ).Count(), 
        /* SNIP - 10-15 more subqueries */        
       }; 

Acorté hasta la consulta para incluir sólo 2 de las subconsultas , pero podría haber alrededor de 10-15 más de ellos. ¿Hay alguna manera de que pueda refactorizar la consulta para limpiar el código? No estoy buscando un aumento en el rendimiento. Quiero simplemente limpiar el código quizás colocando las subconsultas en métodos separados mientras me aseguro de que sea una sola llamada a la base de datos. ¿Esto es posible?

+0

Si desea un código más limpio, quizás desee considerar la creación de un procedimiento almacenado en su base de datos – Mathieu

+0

@Mathieu ¿Es esa la única manera? – Dismissile

Respuesta

2

que sólo puede ofrecer minimizar su longitud por algo como esto (mediante el uso de let palabra clave en su búsqueda original):

var subQuery = from a in s.Applications 
        from e in a.Summaries 
        select e; 

También usted puede tener algunos refactors como:

subQuery.Count(e=>e.CreationDate >= today && e.CreationDate < tomorrow); 

subQuery.max(e=>e.CreationDate); 

De hecho usa notación de puntos y mueve tu consulta a la función relacionada en lugar de a la cláusula where adicional.

y utilizar subQuery en su consulta:

  from s in db.Systems 
      orderby s.Name 
      let subQuery = from a in s.Applications 
        from e in a.Summaries 
        select e 
      select new SystemSummary 
      { 
       Id = s.Id, 
       Code = s.Code, 
       Name = s.Name, 
       LastException = subQuery.max(e=>e.CreationDate), 
       TodaysExceptions = subQuery.Count(e=>e.CreationDate >= today 
              && e.CreationDate < tomorrow), 
       /* SNIP - 10-15 more subqueries */        
      }; 

Esto sigue siendo una sola llamada a la base de datos.

+0

@Dismissile, me perdí 's', edité la respuesta, puedes usar' let' para simular esta manera en tu consulta. –

+0

Me gusta este enfoque. – Dismissile

+0

Espero que esta ayuda, bueno también vea su comentario :) –

0

Realmente no hay ningún problema para separar su consulta en varios métodos. Sin embargo, hay algunas condiciones.

Asegúrese de que su consulta es IEumerable. Esto es por defecto.

IEnumerable garantiza que la consulta se almacena en la variable, pero no se ejecuta. El compilador optimiza sus consultas en tiempo de ejecución.

rápido y sucio Ejemplo:

private MyContext context = new MyContext() 
private IEnumerable<User> getUser(Guid userID) 
{ 
    return context.User.Where(c => c.ID == userID); 
} 

private void evaluateUser() 
{ 
    bool isUserActive getUser().Any(c => c.IsActive) 
} 

se puede ver que la consulta se divide en dos métodos. Todavía hay una sola llamada al DB porque un IEnumerable almacena la consulta y no el resultado. La consulta se ejecuta solo cuando es necesario.

+2

Creo que te refieres a IQueryable, no a IEnumerable.IEnumerable lo convertirá en una consulta en un objeto en memoria. –

0

Es posible que desee considerar el uso de la palabra clave let para crear "variables" locales para la consulta (esto finalmente termina siendo sus subconsultas). Por ejemplo:

var systems = from s in db.Systems 
       orderby s.Name 
       let lastException = (from a in s.Applications from e in a.Summaries select e.CreationDate).Max() 
       ... 

Otra opción que podría hacer es, posiblemente, crear una sub consulta de las diferentes asociaciones de buenas a primeras, y trabajar con esos elementos.

var systems = from s in db.Systems 
       orderby s.Name 
       from summaries in 
        (from ta in s.Applications 
        from te in ta.Summaries 
        ... 
        select { APPS = ta, SUMMS = te ,/*anything else you want*/ }) 
       let lastExpire = (from summaries select SUMMS.CreationDate).Max() 

Infierno, incluso se podría dejar el vamos a cabo en el segundo ejemplo, y sólo tiene que utilizar los summaries entidades en sus selecciona finales. Puede que tenga que jugar un poco para asegurarse de que no obtiene ninguna duplicación de valores, pero al menos de esta manera puede hacer una selección directa en su summaries en lugar de volver a escribir sus subconsultas cada vez.

Cuestiones relacionadas