C# 创建公共谓词函数

C# 创建公共谓词函数,c#,linq-to-sql,C#,Linq To Sql,首先,我不知道该用什么术语来问这个问题,这可能就是为什么我在搜索自己时没有找到答案的原因 因此,我正在使用Linq to SQL(C#,.Net 4),我希望得到一个符合条件的所有用户的列表,我将对其进行如下操作: var users = DataContext.Users.Where(x => x.Criteria1 == "something"); static bool CheckForFlags(User user) { return user.Flag1 || user.

首先,我不知道该用什么术语来问这个问题,这可能就是为什么我在搜索自己时没有找到答案的原因

因此,我正在使用Linq to SQL(C#,.Net 4),我希望得到一个符合条件的所有用户的列表,我将对其进行如下操作:

var users = DataContext.Users.Where(x => x.Criteria1 == "something");
static bool CheckForFlags(User user)
{
   return user.Flag1 || user.Flag2 || user.Flag3 || user.Flag4 || user.Flag5;
}
var users = DataContext.Users.Where(x => x.Criteria1 == "something");
    .ToEnumerable()
    .Where(x => CheckForFlags(x));
public static class Predicates
{
    public static Expression<Func<User, bool>> CheckForFlags()
    {
        return (user => user.Flag1 || user.Flag2 || user.Flag3 ||
                        user.Flag4 || user.Flag5);
    }

    public static Expression<Func<User, bool>> CheckForCriteria(string value)
    {
        return (user => user.Criteria1 == value);
    }
}
但在这种情况下,我需要匹配几个字段,问题是这些特定字段是一个常见的检查,我希望能够创建一个专用函数,我可以在任何用户查询中使用该函数来检查是否匹配

为了更好地解释这一点,让我们举一个例子:假设一个用户有5个标志,我想做一个常规检查,看看是否设置了这些标志中的任何一个。所以我可以这样写我的查询:

var users = DataContext.Users.Where(x => x.Flag1 || x.Flag2 || x.Flag3 || x.Flag4 || x.Flag5);
但我想做的是分离出“5标志检查”,这样我也可以在其他查询中使用它,最终我想使用类似于:

var users = DataContext.Users.Where(x => x.Criteria1 == "something" && CheckForFlags(x));
我尝试过这样一个函数:

var users = DataContext.Users.Where(x => x.Criteria1 == "something");
static bool CheckForFlags(User user)
{
   return user.Flag1 || user.Flag2 || user.Flag3 || user.Flag4 || user.Flag5;
}
var users = DataContext.Users.Where(x => x.Criteria1 == "something");
    .ToEnumerable()
    .Where(x => CheckForFlags(x));
public static class Predicates
{
    public static Expression<Func<User, bool>> CheckForFlags()
    {
        return (user => user.Flag1 || user.Flag2 || user.Flag3 ||
                        user.Flag4 || user.Flag5);
    }

    public static Expression<Func<User, bool>> CheckForCriteria(string value)
    {
        return (user => user.Criteria1 == value);
    }
}
但我有一个错误:

“方法‘Boolean CheckForFlags(用户)’不支持转换为 SQL。”

…这是有道理的,但我能做些什么来让它按照我想要的方式工作吗?或者这是一个限制,因为我正在使用LINQtoSQL,事实上它可以与LINQtoObject一起工作?

我认为这会起作用,我认为我已经在我的LINQtoSQL项目中使用过它,但我无法立即找到一个示例。如果没有,请告诉我


Users
对象上创建新属性,而不是创建函数:

partial class Users {
   bool CheckForFlags
    {
       get { 
          return Flag1 || Flag2 || Flag3 || Flag4 || Flag5;
       }
    }
}
那你应该能做到

var users = DataContext.Users.Where(x => x.CheckForFlags);

据我所知,有两种方法可以做到这一点

快速简单的方法是在SQL执行后过滤结果,如下所示:

var users = DataContext.Users.Where(x => x.Criteria1 == "something");
static bool CheckForFlags(User user)
{
   return user.Flag1 || user.Flag2 || user.Flag3 || user.Flag4 || user.Flag5;
}
var users = DataContext.Users.Where(x => x.Criteria1 == "something");
    .ToEnumerable()
    .Where(x => CheckForFlags(x));
public static class Predicates
{
    public static Expression<Func<User, bool>> CheckForFlags()
    {
        return (user => user.Flag1 || user.Flag2 || user.Flag3 ||
                        user.Flag4 || user.Flag5);
    }

    public static Expression<Func<User, bool>> CheckForCriteria(string value)
    {
        return (user => user.Criteria1 == value);
    }
}
然而,就性能而言,这是非常差的。它将返回数据库中仅与第一个条件匹配的所有行,然后在客户端的内存中过滤结果。功能性的,但远不是最佳的

第二个性能更高的选项是在数据库本身上创建一个UDF,并从LINQ调用它。例如,见。明显的缺点是,它将代码移动到数据库中,这是没有人喜欢做的(有很多正当理由)


可能还有其他可行的解决方案,但我只知道这两个。

您尝试过PredicateBuilder吗?我已经一年多没有使用它了,但我发现它在编写“或Where”查询时非常有效

他们页面中的一个示例:

IQueryable<Product> SearchProducts (params string[] keywords)
{
  var predicate = PredicateBuilder.False<Product>();

  foreach (string keyword in keywords)
  {
    string temp = keyword;
    predicate = predicate.Or (p => p.Description.Contains (temp));
  }
  return dataContext.Products.Where (predicate);
}
IQueryable SearchProducts(参数字符串[]关键字)
{
var predicate=PredicateBuilder.False();
foreach(关键字中的字符串关键字)
{
字符串temp=关键字;
谓词=谓词。或(p=>p.Description.Contains(temp));
}
返回dataContext.Products.Where(谓词);
}

LINQ to SQL处理表达式的巧妙之处在于,您可以在代码的其他地方构建表达式,并在查询中引用它们。你为什么不试试这样的东西:

var users = DataContext.Users.Where(x => x.Criteria1 == "something");
static bool CheckForFlags(User user)
{
   return user.Flag1 || user.Flag2 || user.Flag3 || user.Flag4 || user.Flag5;
}
var users = DataContext.Users.Where(x => x.Criteria1 == "something");
    .ToEnumerable()
    .Where(x => CheckForFlags(x));
public static class Predicates
{
    public static Expression<Func<User, bool>> CheckForFlags()
    {
        return (user => user.Flag1 || user.Flag2 || user.Flag3 ||
                        user.Flag4 || user.Flag5);
    }

    public static Expression<Func<User, bool>> CheckForCriteria(string value)
    {
        return (user => user.Criteria1 == value);
    }
}

这肯定是LINQ对SQL的限制,LINQ对对象允许任意谓词。当然,问题是,对于SQL来说,什么是一个好的解决方案。您可以使用动态linq像SQL字符串一样构建表达式。您也可以将方法转换为表达式树,但它要复杂得多。@Vlad:您是对的。我不太关心如何使用我尝试过的函数,但更多的是如何获得相同的最终结果这篇文章会有帮助,是的,我想UDF是一个选项,但我想我们只是把“努力”转移到了另一个地方。。。我宁愿每次都键入“5标志检查”(当然,如果需求发生变化,请尽量记住全部检查),也许
。ToEnumerable()
会比
。ToList()
@Vlad稍好一些。我会改变它,虽然我希望我已经足够阻止询问者使用该解决方案。我担心这将需要我改变自动生成的dbml类-这是一个很大的痛苦去做和维护这就是为什么它们被创建为
部分
。只需在同一项目中的一个单独文件中声明一个新的
分部类Users{}
,并将其放在那里。@Vlad,这也是我担心的问题,让我不敢尝试。不过,如果将部分类转换回SQL@Vlad-老实说,这就是为什么我发布了预选赛。我很确定我已经做到了,但我不知道它实际上是如何工作的。至少值得一试。如果没有用,很容易在以后删除。@Bobson:生成一个表达式可能是个好主意吗
public分部类用户{public static Expression CheckForFlags(){return p=>p.Flag1&&p.Flag2&&&…;}
(这个想法从)+1中偷来):组合表达式而不是谓词似乎是一种方法。看起来很有趣。。不幸的是,无论如何对我来说,我不能在这个特定的项目中使用这样的第三方库。。当然,当我选择一个被接受的答案时,我不会考虑“我的项目限制”。though@musefan:查找它,代码非常简单,因此您可以自己重新开发它。实际上,Adam的答案是做同样的事情。这在EntityFrameworkCore3.1中非常有效。谢谢