2009-07-06 10 views
21

Sentí que lo siguiente debería ser posible. No estoy seguro de qué enfoque tomar.condicional incluir en linq a las entidades?

Lo que me gustaría hacer es usar el método de inclusión para dar forma a mis resultados, es decir, definir qué tan largo a lo largo del gráfico de objetos se debe recorrer. pero ... me gustaría que ese cruce sea condicional.

something like... 

dealerships 
    .include(d => d.parts.where(p => p.price < 100.00)) 
    .include(d => d.parts.suppliers.where(s => s.country == "brazil")); 

entiendo que esto no es válida LINQ, de hecho, que es muy mal, pero esencialmente estoy buscando alguna manera de construir un árbol de expresión que devolverá resultados en forma, lo que equivale a ...

select * 
from dealerships as d 
outer join parts as p on d.dealerid = p.dealerid 
    and p.price < 100.00 
outer join suppliers as s on p.partid = s.partid 
    and s.country = 'brazil' 

con énfasis en las condiciones de unión.

Siento que esto sería bastante directo con esql pero mi preferencia sería construir árboles de expresión sobre la marcha.

como siempre, agradecido por cualquier consejo u orientación

+1

¿Alguna vez encontró una solución a esto? Estoy teniendo el mismo problema. –

+0

Todavía no, pero me quedo interesado y continuará a perseguir ... – tim

+0

que estaba buscando lo mismo –

Respuesta

15

Esto debe hacer el truco:

using (TestEntities db = new TestEntities()) 
{ 
    var query = from d in db.Dealership 
       select new 
       { 
        Dealer = d, 
        Parts = d.Part.Where 
        (
         p => p.Price < 100.0 
          && p.Supplier.Country == "Brazil" 
        ), 
        Suppliers = d.Part.Select(p => p.Supplier) 
       }; 

    var dealers = query.ToArray().Select(o => o.Dealer); 
    foreach (var dealer in dealers) 
    { 
     Console.WriteLine(dealer.Name); 
     foreach (var part in dealer.Part) 
     { 
      Console.WriteLine(" " + part.PartId + ", " + part.Price); 
      Console.WriteLine 
       (
       " " 
       + part.Supplier.Name 
       + ", " 
       + part.Supplier.Country 
       ); 
     } 
    } 
} 

Este código le dará una lista de los concesionarios de cada una conteniendo una lista filtrada de las partes. Cada parte hace referencia a un proveedor. La parte interesante es que debe crear los tipos anónimos en la selección en la forma que se muestra. De lo contrario, la propiedad de parte de los objetos del concesionario estará vacía.

Además, debe ejecutar la instrucción SQL antes de seleccionar los distribuidores de la consulta. De lo contrario, la propiedad de la parte de los distribuidores volverá a estar vacía.Por eso puse la llamada ToArray() en la siguiente línea:

var dealers = query.ToArray().Select(o => o.Dealer); 

Pero estoy de acuerdo con Darren que esto puede no ser lo que los usuarios de la biblioteca están esperando.

+2

Una nota sobre esto, probé esto y no funcionó inicialmente. Eventualmente descubrí que tenía la carga lenta activada lo que causaba que no funcionara. Una vez que apagué la carga lenta, funcionó como un amuleto. Gracias Jakob! – msmucker0527

+5

Esto requiere que devuelva un tipo anónimo en lugar del tipo sin formato con las referencias reales. Esto "funciona" pero me deja con ganas de una solución para devolver el objeto sin procesar con el objeto de la colección real filtrado a lo que realmente quería que fuera devuelto. El tipo de devolución anónima funciona con certeza ... pero realmente no es la solución realmente deseada. – VulgarBinary

+0

@VulgarBinary puede devolver una lista mecanografiada de esta manera: ' distribuidores var = query.ToArray() Seleccionar (o => o.Dealer) .ToList();' Simplemente funciona.! –

1

Me estoy perdiendo algo, o no estás sólo en busca de la palabra clave Any?

var query = dealerships.Where(d => d.parts.Any(p => p.price < 100.00) || 
           d.parts.suppliers.Any(s => s.country == "brazil")); 
+2

i no lo creo, como yo lo entiendo, esto sería volver los concesionarios en los que había partes y proveedores que cumplen la condiciones, en lugar de devolver todos los concesionarios, todas las partes que tienen menos de 100 y todos los proveedores en Brasil. Me doy cuenta de que esto debería ser una combinación externa. Creo que lo que estoy tratando de hacer es podar el árbol de resultados en lugar de filtrar los nodos de nivel superior en función de los valores del árbol. – tim

6

¿Estás seguro de que esto es lo que quieres? La única razón por la que pregunto es, una vez que agrega el filtro en las Partes fuera de los concesionarios, sus resultados ya no son concesionarios. Se trata de objetos especiales que, en su mayoría, están muy cerca de los concesionarios (con las mismas propiedades), pero el significado de la propiedad "Piezas" es diferente. En lugar de ser una relación entre concesionarios y repuestos, se trata de una relación filtrada.

O, para decirlo de otra manera, si tiro un concesionario de sus resultados y se pasa a un método que escribí, y luego en mi método que llamo:

var count = dealership.Parts.Count(); 

Estoy esperando para obtener el partes, no las partes filtradas de Brasil, donde el precio es inferior a $ 100.

Si no utiliza el objeto concesionario para pasar los datos filtrados, se vuelve muy fácil. Se vuelve tan simple como:

var query = from d in dealerships 
       select new { DealershipName = d.Name, 
CheapBrazilProducts = dealership.Parts.Where(d => d.parts.Any(p => p.price < 100.00) || d.parts.suppliers.Any(s => s.country == "brazil")) }; 

Si sólo tenía que conseguir los conjuntos filtrados como pediste, probablemente utilizar la técnica que he mencionado anteriormente, y luego usar una herramienta como AutoMapper para copiar los resultados filtrados desde mi clase anónima a la clase real. No es increíblemente elegante, pero debería funcionar.

Espero que ayude! Fue un problema interesante

+0

Gracias Darren Estoy de acuerdo en cuanto a la identidad del objeto! También me preocupa que esto sea exactamente por lo que esto no puede ser respaldado por EF. Lo que realmente estoy tratando de hacer aquí es analizar una cadena de consulta como http://carpartsdb.com/dealerships/parts(price <100)/proveedores (s.country == Brasil) en una consulta la expresión y compruébalo en el EF, manipula en una estrategia de serialización xml personalizada y listo que tienes un motor RestQL (lenguaje de consulta tranquilo). No estoy demasiado atada a EF pero quiero poder tener árboles de abstracción y expresión de modelos y EF es y no quiero desviarme demasiado de la pila MS. – tim

+0

disculpa, mira este texto en la siguiente respuesta donde será más fácil de leer. – tim

0

Sí, eso es lo que quería hacer Creo que la próxima versión de Data Services tendrá la posibilidad de hacer solo esas consultas de LINQ a REST que serían geniales mientras tanto, acabo de cambiar para cargar la inversa e Incluir las relacionadas entidad que va a ser cargado varias veces pero en teoría sólo hay que cargar una vez en el primer Incluir como en este código

return this.Context.SearchHistories.Include("Handle") 
    .Where(sh => sh.SearchTerm.Contains(searchTerm) && sh.Timestamp > minDate && sh.Timestamp < maxDate); 

antes de que intentara cargar para cualquier manejar la searchHistories que coincidían con la lógica, pero no saben cómo usar la lógica Incluir que publicó, por lo tanto, mientras tanto, creo que una búsqueda inversa sería una solución no tan sucia

0

Sé que esto puede funcionar con una sola inclusión. Nunca pruebe con dos opciones, pero vale la pena intentarlo:

dealerships 
    .Include(d => d.parts) 
    .Include(d => d.parts.suppliers) 
    .Where(d => d.parts.All(p => p.price < 100.00) && d.parts.suppliers.All(s => s.country == "brazil")) 
Cuestiones relacionadas