Linq2SQL表达式中代码共享的选项

时间:2009-09-01 12:40:10

标签: linq-to-sql expression-trees

我有一对Linq to SQL查询,它们包含相同的复杂Where子句,具体为:

where ((range.MinimumFrequency <= minFreq &&  minFreq <= range.MaximumFrequency)
|| (range.MinimumFrequency <= maxFreq && maxFreq <= range.MaximumFrequency)
|| (range.MinimumFrequency <= minFreq && maxFreq <= range.MaximumFrequency)
|| (range.MinimumFrequency >= minFreq && maxFreq >= range.MaximumFrequency))

而不是将这些代码复制并粘贴到所有地方,我想将其重构为可以共享的其他内容。我知道我不能用普通方法做到这一点,因为它无法翻译成SQL,但我也无法得到

  

表达&lt; FUNC&LT; ...&GT;&GT;

事情described here也可以。

如果我为了理解我的理智而简化了where子句,我想转向

where (range.MinumumFrequency < minFreq) 

进入表达式,所以我尝试了:

public static Expression<Func<FreqRange, bool>> Overlaps(decimal minMHz, decimal maxMHz)
{
   return (range => range.MinimumFrequency <= minMHz);
}

这似乎是编译,但我似乎无法使where语句工作,我尝试了以下内容:

where FreqRange.Overlaps(minMHz, maxMHz)

但是这给了我一个编译时错误:

  

无法隐式转换类型   'System.Linq.Expressions.Expression&GT;'   'bool'

有什么想法吗?另外,假设我们得到了这个工作,我可以简单地在Expression&lt;中扩展lambda表达式吗? Func&lt;&gt;&gt;包括其他条件?

2 个答案:

答案 0 :(得分:2)

如果使用语言构建LINQ语法,则隐式声明lamba表达式 - lamba表达式为Expression<Func <>> - 因此您不需要where子句要返回表达式,你需要 表达式。

e.g。

var q = from row in myTable
        where row.a < aMax
        select row.b;

// which translates to

var q = myTable.Where(row => row.a < aMax).Select(row => row.b);

现在你需要“缓存”row => row.a < aMax - 而不仅仅是row.a < aMax,如果愿意的话。所以,如果你要写这样的东西......

Expression<Func<?,bool>> cachedExpression = row => row.a < aMax;

var q = from row in myTable
        where cachedExpression
        select row.b;

嗯,你说的是“给定一行,确实是(给定一行,确实row.a小于aMax)”。这是无意义的,如果cachedExpression的类型为Expression<Func<?,bool>>且myTable是LinqToSql提供的表,则无法编译。它会转化为这样的东西:

Expression<Func<?,bool>> cachedExpression = row => row.a < aMax;

//nonsense .Where:
var q = myTable.Where(row => cachedExpression).Select(row => row.b);
//effectively says this:
var q = myTable.Where(row => (row0 => row0.a < aMax)).Select(row => row.b);

现在,这是 legal ,因为linq查询提供程序可能实现具有非布尔值的whereclause,但它是一个非常奇怪的要做的事;当然没有标准的linq提供者(比如Linq to Sql)做这样的事情。

那么应该你做什么?类似的东西:

//not .Where(row=>cachedExpression)!
var q = myTable.Where(cachedExpression).Select(row => row.b);

//or, alternatively:
var q = from filteredRow in myTable.Where(cachedExpression)
        select filteredRow.b;

答案 1 :(得分:1)

您还可以创建一个接收和发送IQueryable的方法。我已经使用了这些视图,但纯粹是在代码中。

public class WithProps
{
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public override string ToString()
    {
        return string.Format("{0}-{1}", Prop1, Prop2);
    }
}

public static class Tools
{
    public static IQueryable<WithProps> MyFilter(this IQueryable<WithProps> props)
    {
        return props.Where(p => p.Prop1 == "hi");
    }
}

class Program
{
    static void Main(string[] args)
    {
        var propList = new List<WithProps>()
        {
            new WithProps(){ Prop1="hi", Prop2="there"},
            new WithProps(){ Prop1="hi", Prop2="bye"},
            new WithProps(){ Prop1="hello", Prop2="world"},
            new WithProps(){ Prop1="bye", Prop2="friend"}
        };

        var filtered = propList.AsQueryable().MyFilter();

        Console.WriteLine("propList ===");
        foreach (var item in propList)
            Console.WriteLine(item.ToString());
        Console.WriteLine("filtered ===");
        foreach (var item in filtered)
            Console.WriteLine(item.ToString());
    }
}

...结果...

propList ===
hi-there
hi-bye
hello-world
bye-friend
filtered ===
hi-there
hi-bye