跟踪.ToListing EF Linq查询之前匹配的表达式数量 [英] Tracking number of expressions matched before .ToListing an EF Linq Query

查看:51
本文介绍了跟踪.ToListing EF Linq查询之前匹配的表达式数量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

说我在查询的select语句中:

Say I am in the select statement of a query:

context.Orders.Where(expr).Select(o => new OrderInfo{ ... }).ToList();

我有一个表达式列表(在expr中表示的一个子集),但不是所有的表达式都可以满足(想法是它们都是在OrElse条件下添加的表达式):

And I have a list of expressions ( a subset of what is expressed in expr ), not all which will be fulfilled ( the idea being they were all the ones added under the OrElse condition ):

List<Expression<Func<Order, bool>>> exprAppliedList;

您可以想象他们是这样的:

You could imagine them like this:

o => o.BillingFirstName == xStr,
o => o.BillingLastName == xStr,
o => o.ShippingFirstName == xStr,
o => o.ShippingLastName == xStr,

o => o.BillingFirstName == yStr,
o => o.BillingLastName == yStr,
o => o.ShippingFirstName == yStr,
o => o.ShippingLastName == yStr,

诀窍是,在我.ToList()之前,我想以某种方式索引我以该奇数顺序匹配的或表达式中的多少. 如果我输入"John",并且有人输入BillingFirstName = "John" BillingLastName = "John", ShippingFirstName = "John", and ShippingLastName = "John",我可以给他分配SearchValue = 4

The trick is, before I .ToList() it, I would like to somehow index how many, of the or Expressions I matched on that singular order. If I type in "John" and someone has BillingFirstName = "John" BillingLastName = "John", ShippingFirstName = "John", and ShippingLastName = "John" I could assign him something like SearchValue = 4,

我有很多原因想要在.ToList()之前执行此操作,其中一个原因是我实际上只需要过滤即可,而不需要这些字段.另一个是如果要在事后进行比较,我将不得不生成一组不同的表达式,因为它们的类型将改为List<Expression<Func<OrderInfo, bool>>>.

I have a lot of reasons for wanting to do this before .ToList(), one being I simply don't actually need those fields other than by filtering. Another is that I would have to generate a set of different expressions if I wanted to compare it after the fact since they would be of type List<Expression<Func<OrderInfo, bool>>> instead.

我意识到EF Linq不会接受任何废话,自定义功能甚至很多扩展方法.但这已经是lambda表达式的列表,这似乎是Linq早餐吃的东西.

I realize that EF Linq won't accept any nonsense, custom functions or even a lot of extension methods. But this is already a list of lambda expressions, which seems to be what Linq eats for breakfast.

我确实认为我可以在ToList()ing之后做得到,也许每个人都会说那是我应该做的..但是我也很好奇是否有可能,因为我是一个很好奇的人.我认为它在许多情况下都可能有用.

I do think I could probably do it after ToList()ing , and perhaps everyone will just say that is what I should do.. But I am also curious if it is possible, because I am a curious guy.. And I think it could be useful in a lot of scenarios.

我尝试了一些奇怪的事情:

I tried something wacky like:

context.Orders.Where(expr).Select(o => new OrderInfo
{ 
  ... 
  SearchValue = ExpressionMatchList.Any() 
     ? ExpressionMatchList.Count(e => e.Compile().Invoke(o)) 
     : 0,
}).ToList();

但随后Linq在某个地下城层爆炸.最有意义的错误似乎是说

but then Linq exploded at some sub-dungeon layer. The most meaningful error seemed to say something like

'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)

无论如何...我很感谢您对此的想法和考虑,即使事实证明这是一个完全不明智的工作构想.

Anyway... I appreciate your thoughts and consideration on this, even if it turns out to be a totally whack job idea..

推荐答案

可能,但是您需要更深入地研究表达式树.

It's possible, but you need to dig more deeper into expression trees.

首先,您需要的是这样的动态表达式:

First, what you need is a dynamic expression like this:

(match1 ? 1 : 0) + (match2 ? 1 : 0) + ... + (matchN ? 1 : 0)

然后,您需要将其插入选择器表达式.您可以通过使用Expression类方法手动创建选择器来实现困难的方法,或者通过使用所谓的原型表达式来轻松实现.它们是带有附加参数的编译时lambda表达式,用作占位符,后来被其他表达式替换.

Then you need to plug it into the selector expression. You can do that in a hard way by manually creating the selector using the Expression class methods, or much easier by using what I call prototype expressions. They are compile time lambda expressions with additional parameters which serve as placeholders and later are replaced with other expressions.

在任何一种情况下,您都需要一个辅助方法来用另一个表达式替换参数表达式(例如,确保所有ExpressionMatchList项都使用一个相同的参数实例,这在EF6中很重要).将其视为等同于string.Replace的表达式.有很多示例说明如何实现(均基于ExpressionVisitor),这是我使用的示例:

In either case you need a helper method for replacing a parameter expression with another expression (for instance to make sure all the ExpressionMatchList items use one and the same parameter instance, which is important in EF6). Think of it as expression equivalent of string.Replace. There are many examples of how to do that (all based on ExpressionVisitor), here is the one I use:

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);
    }
}

现在让我们实现上述概念.

Now let implement the aforementioned concept.

首先,原型表达式:

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];

这是您的原始lambda表达式选择器,带有附加的matchCount参数,我们将用所需的表达式替换该参数,该参数是根据ExpressionMatchList动态生成的.让我们这样做:

So it's your original lambda expression selector with additional matchCount parameter which we are going to replace with the desired expression, dynamically built from ExpressionMatchList. Let do that:

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);

现在我们准备创建所需的选择器:

Now we are ready to create the desired selector:

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

并使用它:

var result = context.Orders.Where(expr).Select(selector).ToList();

这篇关于跟踪.ToListing EF Linq查询之前匹配的表达式数量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆