C# 如何重用Linq到Sql查询中的where子句

C# 如何重用Linq到Sql查询中的where子句,c#,.net,linq-to-sql,C#,.net,Linq To Sql,我让用户搜索类型为Record的记录。他们在文本框中键入一个搜索词,然后我通过将几个字段与搜索词匹配来搜索记录 我的查询如下所示: var results = from record in DataContext.Records where record.Field1.ToLower().Contains(term) || record.Field2.ToLower().Contains(te

我让用户搜索类型为Record的记录。他们在文本框中键入一个搜索词,然后我通过将几个字段与搜索词匹配来搜索记录

我的查询如下所示:

var results = from record in DataContext.Records
              where
                   record.Field1.ToLower().Contains(term) ||
                   record.Field2.ToLower().Contains(term) ||
                   record.Field3.ToLower().Contains(term)
              select record;
我有许多查询都使用相同的过滤器,因此我想提取过滤,以便可以重用。比如:

var filter = new Func<Record, string, bool>(
                (record, term) =>
                    record.Field1.ToLower().Contains(term) ||
                    record.Field2.ToLower().Contains(term) ||
                    record.Field3.ToLower().Contains(term)
             );

var results = from record in DataContext.Records
              where filter(record, term)
              select record;
但是,它不起作用,因为:

方法“System.Object DynamicInvokeSystem.Object[]”不支持转换为SQL


如何在查询中重用where条件?

我认为您需要将其变成一个表达式。否则,它试图将实际的C方法调用转换为SQL,而不是它的描述。这并不能保证该版本能够正常工作;我不确定哪些字符串函数可以转换为SQL。

您需要构建表达式而不是函数:

Expression<Func<Record, bool>> filter = 
  record => record.Field1.ToLower().Contains(term);  // rest omitted
编辑以添加:如果希望能够在不同术语上创建过滤器,则只需使用一个方法从术语生成表达式:

private static Expression<Func<Record, bool>> Filter(string term)
{
  return r => r.Field1.ToLower().Contains(term);
}

var results = DataContext.Records.Where(Filter(term));
无论如何,重要的是Where子句中的内容必须是一个表达式——但是如上所示,您可以通过动态构建合适的表达式使表达式依赖于术语。如果您在Where子句中直接拼写出过滤器,这正是LINQtoSQL所要做的。

使用


有关更多信息,请参见。

除了其他人指出的表达问题之外,我建议您进行调查。它非常适合动态组合lambda表达式。

where子句的C语法只是编译器的一部分;它生成相同的代码。是的,但如果筛选器的类型为Expression而不是Func,则它不支持filterrecord语法。您会发现错误过滤器是一个变量,但它的使用方式与方法类似。若你们只是写where过滤器,那个么你们会得到“不能隐式转换类型表达式…”。。。谢谢,这是有道理的。然而,我并没有成功地实现它,因为在我给出的错误示例中,这个术语是lambda表达式中的闭包,要重用过滤器,我需要将它作为一个参数。现在,我的过滤器是一个表达式,其中需要一个表达式。这仍然可行吗?是和否。Where需要表达式,所以您的表达式必须是该类型。但这很容易实现。我将更新答案。虽然我选择了CompiledQuery,但此方法的优点是不将表达式绑定到DataContext。无论如何,我从中学到了很多!我认为这不会像编写的那样工作-CQ.Compile生成一个Func,因此不能在LINQ to SQL查询的Where子句中使用它。在DataContext.Records中,r.Field1.ToLower.Containsterm选择r;那么var results=querysomeTerm;?没有使用CompiledQuery,所以我可能错了@伊托尔森:它确实有效。这就是如何创建可在LINQtoSQL中使用的可重用函数。查询提供程序将识别已编译的函数,并根据需要生成查询。它不仅可以生成完整的查询,还可以生成部分查询,比如这个条件查询。唯一奇怪的是,我还在Linq To Object查询中重用了该过滤器,然后我仍然必须将Linq To Sql DataContext作为参数传递。有没有独立于DataContext定义查询的方法?@Xavier:生成的委托可以像普通的C委托一样使用。它只是增加了在LINQ到SQL查询中工作的额外好处,这些查询涉及到它编译的数据上下文。据我所知,委托的Target属性设置为关联的已编译查询,该查询负责生成查询。我认为您可能能够编译一个适用于任何数据上下文的类,但是您不能使用任何LINQ到SQL类作为参数。只有常见类型int、string等。。我将对此进行探讨,然后再作报告。@Xavier:再想想,这似乎不太合理。它的用途非常有限,几乎一文不值。它只有在与编译它的数据上下文一起使用时才有用。
private static Expression<Func<Record, bool>> Filter(string term)
{
  return r => r.Field1.ToLower().Contains(term);
}

var results = DataContext.Records.Where(Filter(term));
Func<string, Expression<Func<Record, bool>>> filter =
  term => (r => r.Field1.ToLower().Contains(term));

var results = DataContext.Records.Where(filter(term));
var filter = CompiledQuery.Compile(
    (DatabaseDataContext dc, Record record, string term) =>
        record.Field1.ToLower().Contains(term) ||
        record.Field2.ToLower().Contains(term) ||
        record.Field3.ToLower().Contains(term)
);

var results = from record in DataContext.Records
              where filter(DataContext, record, term)
              select record;