Отслеживание количества совпадений, найденных ранее. Список EF Linq Query - PullRequest
0 голосов
/ 14 сентября 2018

Скажите, что я в утверждении select запроса:

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

И у меня есть список выражений (подмножество того, что выражено в expr), но не все, которые будут выполнены (идея состоит в том, что все они были добавлены при условии OrElse):

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

Вы можете представить их так:

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 (), я бы хотел как-то индексировать , сколько , выражений или я соответствовал этому единственному порядку. Если я наберу «Джон» и у кого-то будет BillingFirstName = "John" BillingLastName = "John", ShippingFirstName = "John", and ShippingLastName = "John", я мог бы назначить ему что-то вроде SearchValue = 4,

У меня есть много причин хотеть сделать это до .ToList (), во-первых, мне просто не нужны эти поля кроме фильтрации. Во-вторых, мне пришлось бы генерировать набор различных выражений, если бы я хотел сравнить их после факта, поскольку они будут иметь тип List<Expression<Func<OrderInfo, bool>>>.

Я понимаю, что EF Linq не будет принимать чепуху, пользовательские функции или даже множество методов расширения. Но это уже список лямбда-выражений, который, похоже, и есть то, что ест Линк на завтрак.

Думаю, я мог бы сделать это после ToList (), и, возможно, все просто скажут, что это то, что я должен делать ... Но мне также любопытно, возможно ли это, потому что я любопытный парень ... И Я думаю, что это может быть полезно во многих сценариях.

Я пробовал что-то дурацкое, вроде:

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)

Во всяком случае ... Я ценю ваши мысли и соображения по этому поводу, даже если это окажется совершенно ненормальной идеей работы.

1 Ответ

0 голосов
/ 14 сентября 2018

Это возможно, но вам нужно копать глубже в деревья выражений.

Во-первых, вам нужно динамическое выражение, подобное этому:

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

Тогда вам нужно подключить его к выражению селектора. Вы можете сделать это сложным способом, вручную создавая селектор, используя методы класса Expression, или намного проще, используя то, что я называю выражениями прототипа. Это лямбда-выражения времени компиляции с дополнительными параметрами, которые служат заполнителями, а затем заменяются другими выражениями.

В любом случае вам нужен вспомогательный метод для замены выражения параметра другим выражением (например, чтобы убедиться, что все элементы ExpressionMatchList используют один и тот же экземпляр параметра, что важно в EF6). Думайте об этом как о выражении, эквивалентном string.Replace. Есть много примеров того, как это сделать (все основаны на 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];

Итак, это ваш оригинальный селектор лямбда-выражений с дополнительным параметром matchCount, который мы собираемся заменить на желаемое выражение, динамически построенное из ExpressionMatchList. Давайте сделаем это:

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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...