C# 跟踪之前匹配的表达式数。ToListing EF Linq查询

C# 跟踪之前匹配的表达式数。ToListing EF Linq查询,c#,asp.net,entity-framework,linq,C#,Asp.net,Entity Framework,Linq,假设我在查询的select语句中: context.Orders.Where(expr).Select(o => new OrderInfo{ ... }).ToList(); 我有一个表达式列表(在expr中表示的一个子集),并不是所有的表达式都能实现(想法是它们都是在OrElse条件下添加的): 诀窍是,在I.ToList()之前,我想以某种方式索引我在该单数顺序上匹配的or表达式的数量。 如果我输入“John”,而某人拥有BillingFirstName=“John”Billing

假设我在查询的select语句中:

context.Orders.Where(expr).Select(o => new OrderInfo{ ... }).ToList();
我有一个表达式列表(在
expr
中表示的一个子集),并不是所有的表达式都能实现(想法是它们都是在
OrElse
条件下添加的):

诀窍是,在I.ToList()之前,我想以某种方式索引我在该单数顺序上匹配的or表达式的数量。 如果我输入“John”,而某人拥有
BillingFirstName=“John”BillingLastName=“John”、ShippingFirstName=“John”和ShippingLastName=“John”
我可以给他分配类似于
SearchValue=4的内容

我以前有很多理由想这样做。ToList(),其中一个原因是除了过滤之外,我根本不需要这些字段。另一个原因是,如果我想在事后比较它,我必须生成一组不同的表达式,因为它们将是
List
类型

我意识到EF Linq不会接受任何无意义的、自定义的函数甚至很多扩展方法。但这已经是lambda表达式的列表了,这似乎是Linq早餐吃的东西

我确实认为我可能会在演讲结束后做这件事,也许每个人都会说这是我应该做的。。但我也很好奇这是否可能,因为我是一个好奇的人。。我认为它在很多情况下都很有用

我尝试了一些古怪的东西,比如:

context.Orders.Where(expr).Select(o => new OrderInfo
{ 
  ... 
  SearchValue = ExpressionMatchList.Any() 
     ? ExpressionMatchList.Count(e => e.Compile().Invoke(o)) 
     : 0,
}).ToList();
但后来林克在地下城的某一层爆炸了。最有意义的错误似乎是这样说的

'Unable to process the type '.....[Order].....', because it has no known mapping to the value layer.

> StackTrace: at
> System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.NewArrayInitTranslator.TypedTranslate(ExpressionConverter
> parent, NewArrayExpression linq)

无论如何。。。我很感激你对这一点的想法和考虑,即使这是一个完全不正常的工作想法。

这是可能的,但你需要更深入地挖掘表达树

首先,您需要这样一个动态表达式:

(match1 ? 1 : 0) + (match2 ? 1 : 0) + ... + (matchN ? 1 : 0)
然后需要将其插入选择器表达式。您可以通过使用
Expression
类方法手动创建选择器,或者通过使用我称之为原型表达式的方法更容易地实现这一点。它们是编译时lambda表达式,具有用作占位符的附加参数,稍后将替换为其他表达式

在这两种情况下,您都需要一个助手方法来用另一个表达式替换参数表达式(例如,确保所有
ExpressionMatchList
项使用同一个参数实例,这在EF6中很重要)。将其视为与
字符串等价的表达式。替换
。有许多例子说明了如何做到这一点(都基于
ExpressionVisitor
),下面是我使用的一个例子:

public static class ExpressionExtensions
{
    public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
    {
        return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
    }

    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node) =>
            node == Source ? Target : base.VisitParameter(node);
    }
}
现在让我们实现上述概念

首先,原型表达式:

Expression<Func<Order, int, OrderInfo>> selectorPrototype = (o, matchCount) => new OrderInfo
{
    // ...
    SearchValue = matchCount,
};
var selectorBody = selectorPrototype.Body;
var selectorParameter = selectorPrototype.Parameters[0];
var matchCountParameter = selectorPrototype.Parameters[1];
现在,我们准备创建所需的选择器:

var selector = Expression.Lambda<Func<Order, OrderInfo>>(selectorBody, selectorParameter);

你不能那样做。IQueryable只是一个查询。像
ToArray()
ToList()
Count()
这样的方法告诉EF将该查询转换为SQL并执行它。在执行SQL语句之前,您无法知道是否会有任何结果。如果你想提前知道计数,你必须使用
.count()
,但是Linq在某个子地下城层爆炸了。
不,这是预期的行为。IQueryable是查询的表示形式。它不是自己执行的。它被传递给提供者执行。该提供程序将IQueryable转换为可针对数据源执行的查询,并运行该查询。这个错误说明的是,您向该查询添加了无法转换为SQLYeah的代码。我确实知道Linq只是在i.ToList()之前编写查询。我想我必须问一个问题,我想要做的事情是否可以先在SQL中完成,这似乎不太可能。这就是
SELECT Count(*)
的目的。如果您调用
.Count(*)
,就会生成这样的结果。如果你问“我能在不实际执行查询的情况下得到结果的数量吗?”答案是否定的。是的,或者更像是,“在Sql中,一个结果是否知道是什么发现了它,更不用说,当你得到它时,哪个或子句发现了它?并且只在一个查询中。”这太棒了。这可能需要我花点时间来深入研究。我喜欢并着迷于表达式的强大功能,所以当我有时间对其进行更广泛的测试时,我会查看并更新它。谢谢
Expression<Func<Order, int, OrderInfo>> selectorPrototype = (o, matchCount) => new OrderInfo
{
    // ...
    SearchValue = matchCount,
};
var selectorBody = selectorPrototype.Body;
var selectorParameter = selectorPrototype.Parameters[0];
var matchCountParameter = selectorPrototype.Parameters[1];
var zero = Expression.Constant(0);
var one = Expression.Constant(1);
var matchCountValue = !ExpressionMatchList.Any() ? zero : ExpressionMatchList
    .Select(match => Expression.Condition(
        match.Body.ReplaceParameter(match.Parameters[0], selectorParameter),
        one, zero))
    .Aggregate<Expression>(Expression.Add);

selectorBody = selectorBody.ReplaceParameter(matchCountParameter, matchCountValue);
var selector = Expression.Lambda<Func<Order, OrderInfo>>(selectorBody, selectorParameter);
var result = context.Orders.Where(expr).Select(selector).ToList();