Выражение для получения LINQ с Содержит в EF для SQL IN (), где для сущностей дочернее свойство равно значению - PullRequest
1 голос
/ 22 марта 2019

У меня есть простая необходимость отфильтровать всех родителей из возвращенной коллекции, где нет совпадений в поле, которое вызывается по имени из строки, не соответствует представленному значению.Что мне нужно, так это если parent объект имеет child объект, а свойство child объектов "foo" (вызывается строкой) не равно или равно значению bar, объект parent фильтруетсяиз коллекции соответственно.

Вот мой linq ef call

var field = "bar";
var values = new List<string>{"foo","fuYu"};
var dataPage = _aim_context.ae_s_bld_c.AsNoTracking();
var result = dataPage.Where(x => 
                              DbHelper.byPropertyContains(x.udfs, field, values)
                           );
// NOTE `udfs` is a ONE-to-ONE with `ae_s_bld_c`

То, что я ищу, похоже на SQL

SELECT [m].[id],[m.udfs].[bar],
FROM [dbo].[ae_s_bld_c] AS [m]
    INNER JOIN [dbo].[ae_s_bld_c_udf] AS [m.udfs]
        ON ([m].[multitenant_id] = [m.udfs].[multitenant_id])
WHERE ([m].[multitenant_id] = 1.0)
    AND ([m.udfs].[bar] IN ('foo','fuYu')) --< Goal line

То, как я подошел к этому, состояло в том, чтобы получить выражение, настроенное для принятия List<string> и создания SQL.Я прочитал около 50 статей и SO сообщений, но точно не понял, почему я этого не понимаю, поскольку у всех, похоже, есть разные идеи, и большинство из них не соответствуют ядру dotnet 2.1+.

Вот то, чем я сейчас занимаюсь после многих итераций. ПРИМЕЧАНИЕ: это немного отличается от того, что я делаю после того, как я даю свой текущий след.

Мой текущий контекст linq try

//...
dataPage = dataPage.Where(DbHelper.byPropertyContains<ae_s_bld_c>("udfs", field, values));
//...

Думаю, было бы лучше, если бы это был первый пример, который я выложил, но это то, что я получилс тех пор как у меня было время выровнять его с x=>x.udfs, как x=> funName(x.udfs) и x=> x.udfs.funName()

Мой статический метод для построения выражения

public static class DbHelper
{
    public static Expression<Func<T, bool>> byPropertyContains<T>(string node, string field, List<string> value) {
//trying to take parent item and get it's property by string name because
// doing the function in linq like x=>x.udfs was not working right
// but that is the prefered I think
        var property_parameter = Expression.Parameter(typeof(T), "x");
        var property = Expression.PropertyOrField(property_parameter, node);
        var selector_parameter = Expression.Parameter(property.Type, "y");
        var selector = Expression.PropertyOrField(selector_parameter, field);
        var methodInfo = typeof(List<string>).GetMethod("Contains", new Type[] {
            typeof(string)
        });
        var list = Expression.Constant(value, typeof(List<string>));
        var body = Expression.Call(methodInfo, list, selector);
        return Expression.Lambda<Func<T, bool>>(body, selector_parameter);
    }
}

Обновление

По запросу @NetMage я пытался работать в обратном направлении с LINQpad.Я думаю, что я близко, но трудно сказать с выходом.Я помещаю это здесь для справки.Чтобы было понятно, имя свойства дочернего элемента будет строкой имени.Наилучшим результатом будет то, что у меня может быть имя типа udfs.foo, где я могу протестировать на любом уровне, если значения содержатся по имени строки, но на самом деле все в порядке, начиная с этого здесь,

var result = dataPage.Where(x => 
                              DbHelper.byPropertyContains(x.udfs, field, values)
                           );

output of LINQpad

1 Ответ

1 голос
/ 22 марта 2019

Давай начнем отсюда. Вам нужен эквивалент чего-то подобного

var result = dataPage.Where(x => values.Contains(x.udfs.{field}));

где field - это строка, возвращающая свойство, динамически определяемое именем.

В EF Core вам даже не нужно иметь дело с созданием выражений вручную, потому что EF Core предоставляет специальную переводимую функцию SQL для доступа к простым свойствам по имени EF.Property .

С этим методом решение простое:

var result = dataPage
   .Where(x => values.Contains(EF.Property<string>(x.udfs, field)));
...