У меня есть простая необходимость отфильтровать всех родителей из возвращенной коллекции, где нет совпадений в поле, которое вызывается по имени из строки, не соответствует представленному значению.Что мне нужно, так это если 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](https://i.stack.imgur.com/4HKK2.png)