Дерево выражений для groupby с предложением where, а затем select

Из динамического столбца пользовательского интерфейса поступают в качестве параметра в API и на основе параметра я должен получать данные из базы данных. Пример. В приведенном ниже коде на основе столбца выполняется условие linq-запроса. Теперь я хочу сделать его универсальным, чтобы он служил, если в будущем появится новое условие столбца.

public List<string> GetFilteredTypeAhead(string searchText,string searchForRole,int fiscalyear,int fiscalPeriod)
        {
 if (searchForRole == "column1")
            {
                var accounts = (from a in _context.Account
                                where a.column1.StartsWith(searchText) && a.FiscalPeriod == fiscalPeriod && a.FiscalYear ==fiscalyear
                                group a.column1 by a.column2 into g
                                select g.Key).ToList();
                return accounts;
            }
            else if (searchForRole == "column2")
            {
                var accounts = (from a in _context.Account
                                where a.column2.StartsWith(searchText) && a.FiscalPeriod == fiscalPeriod && a.FiscalYear == fiscalyear
                                group a.column2 by a.column2 into g
                                select g.Key).ToList();
                return accounts;
            }
            else if (searchForRole == "column3")
            {
                var accounts = (from a in _context.Account
                                where a.column3.StartsWith(searchText) && a.FiscalPeriod == fiscalPeriod && a.FiscalYear == fiscalyear
                                group a.column3 by a.column3 into g
                                select g.Key).ToList();
                return accounts;
            }
            else if (searchForRole == "column4")
            {
                var accounts = (from a in _context.Account
                                where a.column4.StartsWith(searchText) && a.FiscalPeriod.Equals(fiscalPeriod) && a.FiscalYear.Equals(fiscalyear)
                                group a.column4 by a.column4 into g
                                select g.Key).ToList();
                return accounts;
            }
            else
            {
                return new List<string>();
            }
        }

Чтобы преобразовать его в универсальный. Я создал дерево выражений.

static IQueryable<T> ConvertToExpression<T>(IQueryable<T> query, string propertyValue, PropertyInfo propertyInfo, int fiscalyear, int fiscalPeriod)
        {
            ParameterExpression e = Expression.Parameter(typeof(T), "e");
            MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
            ConstantExpression c = Expression.Constant(propertyValue, typeof(string));
            MethodInfo mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
            Expression call = Expression.Call(m, mi, c);

            PropertyInfo propertyInfoFiscalPeriod = typeof(T).GetProperty("FiscalPeriod");
            MemberExpression memberPropertyFiscalPeriod = Expression.Property(e, propertyInfoFiscalPeriod);
            ConstantExpression right = Expression.Constant(fiscalPeriod);
            Expression equalsFiscalPeriod = Expression.Equal(memberPropertyFiscalPeriod, Expression.Convert(right, typeof(Int16)));

            PropertyInfo propertyInfoFiscalYear = typeof(T).GetProperty("FiscalYear");
            MemberExpression memberPropertyFiscalYear = Expression.Property(e, propertyInfoFiscalYear);
            right = Expression.Constant(fiscalyear);
            Expression equalsFiscalYear = Expression.Equal(memberPropertyFiscalYear, Expression.Convert(right, typeof(Int16)));

            Expression combineExpression = Expression.And(equalsFiscalPeriod, equalsFiscalYear);

            Expression predicateBody = Expression.And(call, combineExpression);

            Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(predicateBody, e);
            return query.Where(lambda);
        }

И чтобы назвать это, я использовал код, как показано ниже «searchForRole» входит в качестве параметра в «column1», «column2» и т. д.


 PropertyInfo propertyInfo = typeof(Account).GetProperty(searchForRole);

            IQueryable<Account> query = _context.Account;

            query = ConvertToExpression(query, searchText, propertyInfo,fiscalyear,fiscalPeriod);



            var list = query.ToList();

Теперь это работает нормально, но в результате дублируются записи. Я хотел иметь какой-то отдельный или сгруппированный по столбцу переданных параметров. Простыми словами я хотел удалить условие if и сделать мой метод поиска универсальным. Пожалуйста помоги.

Лучший способ динамически запрашивать — использовать библиотеку System.Linq.Dynamic, которую вы можете найти в Nuget.

Harisyam M 31.05.2019 10:22
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
46
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это возможно, но ИМХО лучше свести динамические части к минимуму и максимально использовать безопасность времени компиляции С#.

Рассматриваемый пример запроса

var accounts = (from a in _context.Account
                where a.column1.StartsWith(searchText) && a.FiscalPeriod == fiscalPeriod && a.FiscalYear ==fiscalyear
                group a.column1 by a.column1 into g
                select g.Key).ToList();

можно переписать следующим образом

var accounts = _context.Account
    .Where(a => a.FiscalPeriod == fiscalPeriod && a.FiscalYear == fiscalyear)
    .Select(a => a.column1)
    .Where(c => c.StartsWith(searchText))
    .Distinct()
    .ToList();

Как видите, единственная динамическая часть — это a => a.column1 типа Expression<Func<Account, string>>. Итак, все, что вам нужно, это такой метод:

static Expression<Func<T, M>> MemberSelector<T>(string name)
{
    var parameter = Expression.Parameter(typeof(T), "e");
    var body = Expression.PropertyOrField(name);
    return Expression.Lambda<Func<T, M>>(body, parameter);
}

и заменить

.Select(a => a.column1)

с участием

.Select(MemberSelector<Account, string>(searchForRole))

Спасибо. Он выглядит работающим. Просто хотел проверить тип возврата func "M" в выражении - Expression<Func<T, M>> , можем ли мы преобразовать его в пользовательский возвращаемый DTO, например ``` public class FilterDto { public string Value { get; задавать; } открытый текст строки { получить; задавать; } }``` и затем преобразовать в toList()

Santosh Kumar 02.06.2019 08:29

Такие требования лучше предоставлять заранее, потому что они могут потребовать совершенно другого решения. Непонятно, что относится к Value, а что к Text свойству DTO и т. д. Также ограничено место для комментариев. Подумайте о том, чтобы опубликовать еще один вопрос, включая именно то, что вам нужно. Ваше здоровье.

Ivan Stoev 02.06.2019 10:36

Другие вопросы по теме