LINQ到实体 - 构建在多个关系中测试集合的子句 [英] LINQ to entities - Building where clauses to test collections within a many to many relationship

查看:112
本文介绍了LINQ到实体 - 构建在多个关系中测试集合的子句的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我正在使用Linq实体框架。我有2个实体:内容标签。他们彼此间有着多对多的关系。 内容可以有很多标签标签可以有很多内容。所以我试图写一个查询来选择任何标签名称等于 blah的所有内容



实体两者都将其他实体的集合作为属性(但不包含ID)。这是我奋斗的地方。我有一个自定义表达式包含(所以,无论谁帮助我,你可以假设我可以为集合做一个包含)。我得到了这个表达式: http://forums.microsoft。 com / MSDN / ShowPost.aspx?PostID = 2670710& SiteID = 1



编辑1



我结束了找到我自己的答案。

解决方案

阅读关于 PredicateBuilder ,阅读人们发给我的所有精彩帖子,在其他网站上张贴,然后阅读更多关于组合谓词规范功能映射 ..哦和我的照片从在LINQ查询中调用函数(其中一些类别是从这些类中获取的)页)。



我最终有一个解决方案!虽然有一部分被黑客攻击...



让我们得到被黑客的一块:(



<我不得不使用反射器并复制标记为内部的ExpressionVisitor类,然后不得不对其进行一些小的更改,以使其正常工作,我必须创建两个异常(因为它是新的内部异常我还要更改ReadOnlyCollection()方法的返回值:

  return sequence.ToReadOnlyCollection< Expression>(); 

To:

  return sequence.AsReadOnly(); 

我会发布类,但它是相当大的,我不要再混淆这篇文章,我希望将来可以从我的图书馆中删除该类,微软将会公开,继续...



我添加了一个ParameterRebinder类:

  public class ParameterRebinder: ExpressionVisitor {
private readonly Dictionary< ParameterExpression,ParameterExpression>地图;

public ParameterRebinder(Dictionary< ParameterExpression,ParameterExpression> map){
this.map = map?新字典< ParameterExpression,ParameterExpression>();
}

public static Expression ReplaceParameters(Dictionary< ParameterExpression,ParameterExpression> map,Expression exp){
return new ParameterRebinder(map).Visit(exp);
}

内部重写表达式访问参数(ParameterExpression p){
ParameterExpression replacement;
if(map.TryGetValue(p,out replacement)){
p = replacement;
}
return base.VisitParameter(p);
}
}

然后我添加了一个ExpressionExtensions类:

  public static class ExpressionExtensions {
public static Expression< T>
//构建参数映射(从第二个参数到第一个参数的参数)$ b(第一个表达式,第二个表达式,表达式,< T> $ b var map = first.Parameters.Select((f,i)=> new {f,s = second.Parameters [i]})ToDictionary(p => ps,p => pf);

//用第一个
var secondBody = ParameterRebinder.ReplaceParameters(map,second.Body)中的参数替换第二个lambda表达式中的参数;

//将lambda表达式的组合应用于第一个表达式的参数
返回Expression.Lambda< T>(merge(first.Body,secondBody),first.Parameters);
}

public static Expression< Func< T,bool>>和< T>(此表达式< Func< T,bool>>首先,表达式< Func< T,bool>>秒){
return first.Compose(second,Expression.And);
}

public static Expression< Func< T,bool>>或者< T>(这个表达式< Func< T,bool>>首先,表达式< Func< T,bool>>秒){
return first.Compose(second,Expression.Or);
}
}

我添加的最后一个类是PredicateBuilder: p>

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

}

这是我的结果...我能够执行此代码并从我正在搜索的标签中获取具有匹配标签实体的结果内容实体!

  public static IList< Content> GetAllContentByTags(IList< Tag>标签){
IQueryable< Content> contentQuery = ...

表达式< Func< Content,bool>>> predicate = PredicateBuilder.False< Content>();

foreach(在标签中标记个人标签){
标签tagParameter = individualTag;
predicate = predicate.Or(p => p.Tags.Any(tag => tag.Name.Equals(tagParameter.Name)));
}

IQueryable< Content> resultExpressions = contentQuery.Where(predicate);

return resultExpressions.ToList();
}

如果有人需要帮助,请让我知道,如果你愿意像我这样给你发送文件,或者只需要更多的信息。


So, I am using the Linq entity framework. I have 2 entities: Content and Tag. They are in a many-to-many relationship with one another. Content can have many Tags and Tag can have many Contents. So I am trying to write a query to select all contents where any tags names are equal to blah

The entities both have a collection of the other entity as a property(but no IDs). This is where I am struggling. I do have a custom expression for Contains (so, whoever may help me, you can assume that I can do a "contains" for a collection). I got this expression from: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2670710&SiteID=1

Edit 1

I ended up finding my own answer.

解决方案

After reading about the PredicateBuilder, reading all of the wonderful posts that people sent to me, posting on other sites, and then reading more on Combining Predicates and Canonical Function Mapping.. oh and I picked up a bit from Calling functions in LINQ queries (some of these classes were taken from these pages).

I FINALLY have a solution!!! Though there is a piece that is a bit hacked...

Let's get the hacked piece over with :(

I had to use reflector and copy the ExpressionVisitor class that is marked as internal. I then had to make some minor changes to it, to get it to work. I had to create two exceptions (because it was newing internal exceptions. I also had to change the ReadOnlyCollection() method's return from:

return sequence.ToReadOnlyCollection<Expression>();

To:

return sequence.AsReadOnly();

I would post the class, but it is quite large and I don't want to clutter this post any more than it's already going to be. I hope that in the future that class can be removed from my library and that Microsoft will make it public. Moving on...

I added a ParameterRebinder class:

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);
        }
    }

Then I added a ExpressionExtensions class:

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);
        }
    }

And the last class I added was 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; }

}

This is my result... I was able to execute this code and get back the resulting "content" entities that have matching "tag" entities from the tags that I was searching for!

    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();
    }

Please let me know if anyone needs help with this same thing, if you would like me to send you files for this, or just need more info.

这篇关于LINQ到实体 - 构建在多个关系中测试集合的子句的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆