如何在使用多个源表时重用“Where”的linq表达式

时间:2011-11-25 02:12:26

标签: c# linq

我们有以下内容:

public interface IOne {
    UInt64 Id { get; }
    Int16 Size { get; }
}

public interface ITwo {
    UInt64 OneId { get; }
    Int16 Color { get; }
}

正如here所解释的那样,重用linq表达式的方法是编写如下内容:

public static Expression<Func<IOne, bool>> MyWhereExpression( int value ){
    return (o) => (o.Size > value);
}

int v = 5;
IQueryable<IOne> records = from one in s.Query<IOne>()
                                        .Where(MyWhereExpression(v))
                           select one;

当我想对两张桌子做同样的事情时,我遇到了问题。

表达式:

public static Expression<Func<IOne, ITwo, bool>> MyWhereExpression2(int color ) {
    return (one,two) => (one.Id == two.OneId) && (two.Color > color );
}

Linq 1:

int color = 100;
IQueryable<IOne> records = from one in s.Query<IOne>()
                           from two in s.Query<ITwo>()
                                        .Where(MyWhereExpression2(color))
                           select one;

这不能用作只能从中仅限于第二个

Linq 2:

int color = 100;
IQueryable<IOne> records = (from one in s.Query<IOne>()
                            from two in s.Query<ITwo>()
                            select new { one, two })
                           .Where(MyWhereExpression2(color));

这导致

  

参数2:无法转换为'Expression&lt; System.Func&lt; IOne,ITwo,bool&gt;&gt;'                                     到'System.Func&lt; AnonymousType#1,int,bool&gt;'

我理解有关AnonymousType的错误消息,但我无法弄清楚如何编写查询。

我想使用表达式而不是只写

的原因
where (one.Id == two.OneId) && (two.Color > color ) 

直接在linq查询中是因为我想在多个linq查询中重用此表达式。

3 个答案:

答案 0 :(得分:3)

目前可能有一个更优雅的解决方案让我感到厌烦,但您可以使用Tuple<IOne, ITwo>而不是匿名类型:

static Expression<Func<Tuple<IOne, ITwo>, bool>> MyWhereExpression2(int color) {
    return t => (t.Item1.Id == t.Item2.OneId) && (t.Item2.Color > color);
}

int color = 100;
IQueryable<IOne> records = (from one in s.Query<IOne>()
                            from two in s.Query<ITwo>()
                            select Tuple.Create(one, two))
                            .Where(MyWhereExpression2(color))
                            .Select(t => t.Item1);

更新: 上面的回答太快了,因为对Tuple.Create的调用无法转换为SQL,因此无法使用Linq to SQL。要使用Linq to SQL,我目前看到的唯一解决方案是创建一个命名类型:

class Pair
{
    public IOne One { get; set; }
    public ITwo Two { get; set; }
}

static Expression<Func<Pair, bool>> MyWhereExpression2(int color) {
    return p => (p.One.Id == p.Two.OneId) && (p.Two.Color > color);
}

int color = 100;
IQueryable<IOne> records = (from one in s.Query<IOne>()
                            from two in s.Query<ITwo>()
                            select new Pair { One = one, Two = two })
                            .Where(MyWhereExpression2(color))
                            .Select(p => p.One);

答案 1 :(得分:1)

首先,为什么你有两个具有相同字段集的表?理想情况下,数据库设计应避免使用不同表中的相同字段名设计应该使用继承。公共字段应该移动到基类中,EF允许您为每个层次结构创建表,这应该可以解决您的问题。

如果使用Table Per Hierarchy创建模型,那么您将不需要接口,并且您的linq查询可以使用共享过滤器表达式。

除非您坐下来编写一个复杂的基于反射的方法,将表达式从一种类型克隆到另一种类型,否则无法实现您的要求。

答案 2 :(得分:1)

回答我自己的问题......

经过一些实验(包括Nick Guerrera的原始答案)后,我采取了另一种方法 - 而不是尝试重用表达式,我重用了整个linq。但是,它仍然需要创建容器结构。

struct Pair {
    public IOne One { get; set; }
    public ITwo Two { get; set; }
    public Pair(IOne one, ITwo two) : this() {
        One = one;
        Two = two;
    }
}


public IQueryable<Pair> Get(ISession s, int color) {
            return from one in s.Query<IOne>()
                   from two in s.Query<ITwo>()
                   where (one.Id == two.OneId) && (two.Color > color)
                   select new Pair(one, two);
        }

现在我可以致电

Get(s, color).Count();

var records = (from data in Get(s, color) select data).Take(2000);