#c# #entity-framework #linq #entity-framework-core
#c# #entity-framework #linq #entity-framework-core
Вопрос:
У меня есть простая необходимость отфильтровать всех родителей из возвращаемой коллекции, где нет совпадения в поле, которое вызывается по имени из строки, не соответствует представленному значению. Что мне нужно, так это то, что если у parent
объекта есть child
object , и это child
свойство objects "foo"
(вызываемое string) не имеет или действительно равно значению bar
, parent
объект соответствующим образом фильтруется из коллекции.
Вот мой вызов linq ef
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 core 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)
);
Комментарии:
1. Я предлагаю получить LINQPad и сравнить ваши
byPropertyContains
результаты с тем, как выглядит созданный вручную лямбда-код (переведенный компилятором вExpression
).2. В LINQPad выполните
Expression<Func<ae_s_bld_c, bool>> f = x => values.Contains(x.udfs.bar);
а затемf.Dump()
и сравните выходные данные с результатом вашего метода сборки.3. Насколько мне известно, не вы определили тип
ae_s_bld_c
, вы определилиvalues
?4. Извините, но я не понимаю, в чем здесь вопрос / проблема. Все, что я вижу, это желаемый SQL и ваш текущий код, но не то, что вы получаете с ним, и в чем проблема — например, SQL, исключение, что-то еще?
5. @IvanStoev Я не могу сообщить вам конкретную ошибку, потому что она будет опубликована только на текущей итерации. Я продолжаю пробовать новые итерации, поэтому я даже не нахожусь на этой версии, скажем. Это отвлекло бы от вопроса, в который я верю в любом случае. Проблема в том, что, судя по всему, я должен иметь возможность выполнять contains для дочернего объекта по имени свойства, но все статьи в SO или просто поиск в Google устарели или неполны, и я продолжаю упускать ключевой фактор, чтобы заставить его работать.
Ответ №1:
Давайте начнем отсюда. Вам нужен эквивалент чего-то подобного
var result = dataPage.Where(x => values.Contains(x.udfs.{field}));
где field
— строка, возвращающая свойство, динамически указанное по имени.
В EF Core вам даже не нужно иметь дело с построением выражений вручную, потому что EF Core предоставляет специальную переводимую функцию SQL для доступа к простым свойствам по имени, называемому EF.Свойство.
С помощью этого метода решение настолько простое:
var result = dataPage
.Where(x => values.Contains(EF.Property<string>(x.udfs, field)));
Комментарии:
1. БОЖЕ… У меня есть google ‘d и google ‘d, и я искал это прямо там все чертово время. Спасибо!!!!