2009-01-12 17 views
8

¿Es posible pasar partes de una consulta de linq a una función? Quiero crear una interfaz común para mi DAL que siempre usa la misma interfaz de consulta. Por ejemplo,En LINQ to SQL, ¿cómo se transfieren partes de una consulta LINQ a una función

List<T> Get(Join j, Where w, Select s){  
    return currentDataContext<T>.Join(j).Where(w).Select(s).ToList();  
} 

¿Es posible este tipo de cosas? Estoy pensando que se haría con árboles de expresiones, pero no he podido encontrar ejemplos de ello.

Respuesta

10

Bueno, el "unir" es complicado, ya que es muy difícil de expresar un join - pero cosas como donde/select/orderby son bastante fáciles ...

Realmente, es solo un caso de combinar los diversos métodos LINQ en IQueryable<T>, que generalmente aceptan Expression<Func<...>> para alguna combinación. Por lo que un básico seleccionar con un predicado opcional sería:

public IQueryable<T> Get<T>(
     Expression<Func<T,bool>> predicate 
     ) where T : class 
    { 
     IQueryable<T> query = (IQueryable<T>)GetTable(typeof(T)); 
     if (predicate != null) query = query.Where(predicate); 
     return query; 
    } 

yo tendería a volver IQueryable<T> también, ya que es totalmente componibles. Si la persona que llama desea una lista, siempre pueden usar ToList() en él ... o (por ejemplo):

using(var ctx = new MyDataContext(CONN)) 
    { 
     ctx.Log = Console.Out; 
     int frCount = ctx.Get<Customer>(c => c.Country == "France").Count(); 
    } 

cuales (usando Neptuno) hace la consulta:

SELECT COUNT(*) AS [value] 
FROM [dbo].[Customers] AS [t0] 
WHERE [t0].[Country] = @p0 

El problema con la inclusión el "seleccionar" (proyección) en la consulta es que terminarías con múltiples tipos genéricos. Como a menudo quiere que la proyección sea de tipo anónimo, sería bastante imposible especificar el tipo de proyección (anónimo) y del tipo de tabla, y no sería invocable.

En realidad, me pregunto si hay mucho beneficio escribiendo este método en absoluto.Yo podría seguir con un método de base:

public IQueryable<T> Get<T>() where T : class 
    { 
     return (IQueryable<T>)GetTable(typeof(T)); 
    } 

Y dejar que la persona que llama lo componen en su forma preferida - tal vez con la sintaxis de consulta:

 var list = (from cust in ctx.Get<Customer>() 
        where cust.Country == "France" 
        select cust.CompanyName).Take(10).ToList(); 

Qué utiliza:

SELECT TOP (10) [t0].[CompanyName] 
FROM [dbo].[Customers] AS [t0] 
WHERE [t0].[Country] = @p0 

Alternativamente, si realmente desea incluir el orden por y la proyección, entonces un método de extensión es el más práctico h; entonces no es necesario especificar el T original (fuente) (que es lo que hace que sea uncallable cuando se mezcla con anon-types):

public static class QueryExtension 
{ 
    public static IQueryable<TProjection> 
     Get<TSource, TProjection, TOrderKey>(
      this IQueryable<TSource> source, 
      Expression<Func<TSource, bool>> where, // optional 
      Expression<Func<TSource, TProjection>> select, 
      Expression<Func<TProjection, TOrderKey>> orderBy) 
    { 
     if (where != null) source = source.Where(where); 
     return source.Select(select).OrderBy(orderBy); 
    } 
} 

luego considerar un método DAL tales como:

public List<string> Countries() 
    { 
     return Customers.Get(
      x=>x.CompanyName != "", 
      x=>x.Country, 
      x=>x).Distinct().ToList(); 
    } 

que utiliza (de nuevo, con Neptuno):

SELECT DISTINCT [t0].[Country] 
FROM [dbo].[Customers] AS [t0] 
WHERE [t0].[CompanyName] <> @p0 
0

Puede usar Dynamic LINQ y pasar los parámetros en forma de cadenas.

2

Compruebe esta clase genérica: TableView.cs.

Básicamente utiliza un TEntity Func <, bool > delegado para aplicar el predicado Dónde:

//... 
public TableView(DataContext dataContext, Expression<Func<TEntity, bool>> predicate) 
{ 
    this.table = dataContext.GetTable<TEntity>(); 
    this.baseQuery = table.Where(predicate); 
    this.predicate = predicate.Compile(); 
} 
//... 
+1

Estoy un poco preocupado por eso, en realidad. El hecho de que compila el predicado significa que siempre cargará toda la tabla y luego hará LINQ-to-Objects sobre él. Eso es ** muy ** ineficiente. –

+0

De acuerdo, tendrá un rendimiento deficiente ... – CMS

1

usted puede utilizar la biblioteca de expresión dinámica disponible con ejemplos Linq, con esta biblioteca de extensión que puede pasar cláusulas LINQ para donde etc ...

Descarga disponible de aquí linq samples

0

Marc Gravell ♦, como de costumbre, proporcionó una respuesta muy perspicaz, pero realmente creo que tienen métodos que toman IQueryables y añaden restricciones funcionaría en la mayoría de los casos y que mantener el código más claro y fácil de mantener. Por ejemplo:

//Join 
public static IQueryable<IContract> AllContracts(this IQueryable<IAccount> accounts, ISession s) { 
      return from a in accounts 
       from contract in s.Query<IContract() 
       where (a.Id == contract.AccountId) 
       select contract; 
    } 
//Where 
public static IQueryable<IContract> Active(this IQueryable<IContract> contracts) { 
      return from contract in contracts 
       where (contract.Active == true) 
       select contract; 
    } 

A continuación, se puede mezclar y combinar éstos así:

IQueryable<IContract> activeContracts = s.Query<IAccount>() 
      .Where(o => o.Name == "XXX") 
      .GetContracts(s) 
      .Active(); 

estoy usando métodos de extensión y LINQ de NHiberante proporcionar consulta método aquí, pero esto podría ser fácilmente reescrito sin métodos estáticos y con cualquier proveedor de LINQ.