Дерево выражений для groupby с предложением where и than select - PullRequest
1 голос
/ 31 мая 2019

Из пользовательского интерфейса динамический столбец приходит в качестве параметра в 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 и сделать мой метод поиска универсальным.Пожалуйста, помогите.

1 Ответ

2 голосов
/ 31 мая 2019

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

Пример запроса

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