LINQ to entities - Edificio donde cláusulas a prueba de colecciones dentro de una relación de muchos a muchos

StackOverflow https://stackoverflow.com/questions/110314

Pregunta

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

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

Edición 1

Terminé de encontrar mi propia respuesta.

¿Fue útil?

Solución

Después de leer acerca de la PredicateBuilder, la lectura de todos los maravillosos mensajes que las personas que me enviaron, la publicación en otros sitios y, a continuación, leer más La Combinación De Predicados y Asignación De La Función Canónica..ah, y he recogido un poco de Las llamadas a las funciones en las consultas de LINQ (algunas de estas clases fueron tomadas de estas páginas).

Por fin tengo una solución!!!A pesar de que hay una pieza que es un poco hackeado...

Vamos a obtener el robo de la pieza con :(

Tuve que usar el reflector y copia de la ExpressionVisitor clase que está marcado como interna.Tuve que hacer algunos cambios de menor importancia para él, para conseguir que funcione.He tenido que crear dos excepciones (porque era newing interna excepciones.Yo también tuve que cambiar el ReadOnlyCollection() método del retorno de:

return sequence.ToReadOnlyCollection<Expression>();

A:

return sequence.AsReadOnly();

Yo iba a publicar la clase, pero es bastante grande y no quiero saturar este post, más de lo que ya va a ser.Espero que en el futuro esa clase puede ser removido de mi biblioteca y que Microsoft lo hará público.De pasar...

He añadido un ParameterRebinder clase:

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 he añadido un ExpressionExtensions clase:

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 se añadió 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...Yo era capaz de ejecutar este código y obtener el resultado "contenido" de las entidades que tienen la coincidencia de "etiquetar" a las entidades de las etiquetas que yo 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, hágamelo saber si alguien necesita ayuda con esto lo mismo, si usted quisiera enviar los archivos para esto, o simplemente necesita más información.

Otros consejos

Resumiendo...

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

Así es que ¿qué estás esperando?

Estoy un poco confundido.

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

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

No estoy seguro de cuál es el proceso de pensamiento era llegar a la interrogador del código, de verdad, y no estoy del todo seguro de lo que es realmente hacer.La única cosa que realmente estoy seguro es de que .AsQueryable() la llamada es completamente innecesario, ya sea .Etiquetas ya es un IQueryable < t>, o la .AsQueryable() es sólo va a fingir para usted -- la adición de llamadas en donde no tiene por qué ser cualquiera.

El error está relacionado con las "etiquetas" de la variable.LINQ to entities no admite un parámetro que es una colección de valores.Simplemente llamando a las etiquetas.AsQueryable() -- como se sugiere en un ealier respuesta-no va a funcionar bien debido a que el defecto en la memoria de consulta de LINQ proveedor no es compatible con LINQ to entities (u otros relacional de los proveedores).

Como solución alternativa, se puede construir manualmente el filtro utilizando la expresión de la API (ver este post del foro) y se aplican 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));
tags.Select(testTag => testTag.Name)

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

NOTA:por favor, edite la pregunta en sí misma, en lugar de responder con una respuesta -- esto no es un hilo de discusión, y se puede volver a la orden de sí mismos en cualquier momento

Si estás en busca de 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 usted podría estar usando LINQ To SQL, en cuyo caso podría ser mejor si usted escribir un procedimiento almacenado para hacer esto:el uso de LINQ para ello probablemente no se ejecutará en SQL Server ... es muy probable que se trate de tirar todo lo de contentQuery y recuperar todos los .Tags las colecciones.Yo tendría que establecer, de hecho, un servidor para comprobar que, a pesar de que.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top