relacion muchos framework consultas consulta c# linq entity-framework lambda many-to-many

c# - framework - relacion muchos a muchos entity



LINQ to entities-Creación de cláusulas para probar colecciones dentro de una relación de muchos a muchos (6)

Esto es lo que la pregunta en sí misma pide:

contentQuery.Where( content => content.Tags.Any(tag => tag.Name == "blah") );

No estoy seguro de cuál fue el proceso de pensamiento para llegar al código del interrogador, realmente, y no estoy del todo seguro de lo que realmente está haciendo. De lo único que estoy realmente seguro es de que la llamada .AsQueryable () es completamente innecesaria: o bien .Tags ya es un IQueryable, o el .AsQueryable () simplemente lo va a falsificar, añadiendo llamadas adicionales donde no es necesario que haya ninguno.

Entonces, estoy usando el marco de la entidad Linq. Tengo 2 entidades: Content y Tag . Están en una relación de muchos a muchos el uno con el otro. Content puede tener muchas Tags y la Tag puede tener muchos Contents . Así que estoy tratando de escribir una consulta para seleccionar todos los contenidos donde los nombres de las etiquetas son iguales a blah

Las entidades tienen una colección de la otra entidad como una propiedad (pero no identificadores). Aquí es donde estoy luchando. Tengo una expresión personalizada para Contains (así que, quien quiera que me ayude, puedes asumir que puedo hacer un "contiene" para una colección). Obtuve esta expresión de: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2670710&SiteID=1

Editar 1

Terminé encontrando mi propia respuesta.


Resumiendo...

contentQuery.Where( content => content.Tags.Any(tag => tags.Any(t => t.Name == tag.Name)) );

Entonces, ¿es eso lo que estás esperando?

Estoy un poco confundido.


tags.Select(testTag => testTag.Name)

¿De dónde se inicializa la variable de etiquetas? ¿Qué es?


NOTA: edite la pregunta en sí misma, en lugar de responder con una respuesta; este no es un hilo de discusión, y pueden reordenarse en cualquier momento.

Si está buscando todos los contenidos que están marcados con cualquiera de un conjunto de etiquetas:

IEnumerable<Tag> otherTags; ... var query = from content in contentQuery where content.Tags.Intersection(otherTags).Any() select content;

Parece que podría estar utilizando LINQ to SQL, en cuyo caso sería mejor si escribe un procedimiento almacenado para hacer esto: usar LINQ para hacerlo probablemente no se ejecute en SQL Server; es muy probable que intente contentQuery todo de contentQuery y contentQuery todas las colecciones .Tags . Sin embargo, tendría que configurar un servidor para verificarlo.


El error está relacionado con la variable ''etiquetas''. LINQ to Entities no admite un parámetro que sea una colección de valores. Simplemente llamando a tags.AsQueryable () - como se sugirió en una respuesta más corta - tampoco funcionará porque el proveedor de consultas LINQ en memoria predeterminado no es compatible con LINQ to Entities (u otros proveedores relacionales).

Como solución alternativa, puede crear manualmente el filtro utilizando la expresión API (consulte esta publicación en el foro ) y aplicarlo de la siguiente manera:

var filter = BuildContainsExpression<Element, string>(e => e.Name, tags.Select(t => t.Name)); var query = source.Where(e => e.NestedValues.Any(filter));


Después de leer acerca de PredicateBuilder , leer todas las publicaciones maravillosas que la gente me envió, publicar en otros sitios, y luego leer más sobre la combinación de Predicados y Mapeo de Funciones Canónicas ... oh y recogí un poco de las funciones de Llamadas en consultas LINQ ( algunas de estas clases fueron tomadas de estas páginas).

¡FINALMENTE tengo una solución! Aunque hay una pieza que está un poco pirateada ...

Vamos a terminar la pieza pirateada con :(

Tuve que usar el reflector y copiar la clase ExpressionVisitor que está marcada como interna. Luego tuve que hacer algunos cambios menores para que funcione. Tuve que crear dos excepciones (porque estaba renovando las excepciones internas. También tuve que cambiar el retorno del método ReadOnlyCollection () desde:

return sequence.ToReadOnlyCollection<Expression>();

A:

return sequence.AsReadOnly();

Publicaba la clase, pero es bastante grande y no quiero saturar esta publicación más de lo que ya va a ser. Espero que en el futuro esa clase pueda ser eliminada de mi biblioteca y que Microsoft la haga pública. Avanzando ...

Agregué una clase ParameterRebinder:

public class ParameterRebinder : ExpressionVisitor { private readonly Dictionary<ParameterExpression, ParameterExpression> map; public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) { this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>(); } public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp) { return new ParameterRebinder(map).Visit(exp); } internal override Expression VisitParameter(ParameterExpression p) { ParameterExpression replacement; if (map.TryGetValue(p, out replacement)) { p = replacement; } return base.VisitParameter(p); } }

Luego agregué una clase ExpressionExtensions:

public static class ExpressionExtensions { public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) { // build parameter map (from parameters of second to parameters of first) var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); // replace parameters in the second lambda expression with parameters from the first var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); // apply composition of lambda expression bodies to parameters from the first expression return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters); } public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.And); } public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.Or); } }

Y la última clase que agregué fue PredicateBuilder:

public static class PredicateBuilder { public static Expression<Func<T, bool>> True<T>() { return f => true; } public static Expression<Func<T, bool>> False<T>() { return f => false; } }

Este es mi resultado ... Pude ejecutar este código y recuperar las entidades de "contenido" resultantes que tienen entidades de "etiqueta" coincidentes de las etiquetas que estaba buscando.

public static IList<Content> GetAllContentByTags(IList<Tag> tags) { IQueryable<Content> contentQuery = ... Expression<Func<Content, bool>> predicate = PredicateBuilder.False<Content>(); foreach (Tag individualTag in tags) { Tag tagParameter = individualTag; predicate = predicate.Or(p => p.Tags.Any(tag => tag.Name.Equals(tagParameter.Name))); } IQueryable<Content> resultExpressions = contentQuery.Where(predicate); return resultExpressions.ToList(); }

Por favor, avíseme si alguien necesita ayuda con esta misma tarea, si desea que le envíe archivos para esto, o simplemente necesita más información.