mssql->entity framework
От: dmitry251  
Дата: 24.09.24 13:27
Оценка:
Всем привет.
переписываю тут проект на EF. Столкнулся с проблемой, которую никак не могу придумать как решить.
Есть запрос, в который в WHERE подставляется условие в зависимости от того, хотим ли мы сделать выборку по аккаунту, или по группе пользователей или по пользователю.
В примере который я приведу ниже, этот запрос обращается к таблице Table1, но таких таблиц около 30 и каждый раз запрос разный, ответ БД (структура) разные, но условие WHERE одно и то же.
Сейчас в программе в каждом контроллере идет вызов функции GetWhere, которая в зависимости от параметров формирует строку, которая подставляется в один из 30+ запросов (их значительно больше 30, думаю что не меньше 200).
И я не знаю как это переписать универсально, с тем чтобы осталось какое то подобие GetWhere, одинаково подходящее к всем переписанным запросам.

Если фильтровать после основного запроса структуру вида IQuerable то вылезают ошибки изза неизвестных типов. В общем я не особо профессионал, но как-то проблему надо решить, не хочется этот момент каждый раз делать руками...

SELECT a.StrParam1, a.StrParam2, SUM(a.IntParam1), SUM(a.IntParam2), MIN(a.ID), 
MIN(a.Date1), MIN(a.StrParam3), 
NULL, MIN(u.StrParam4), MIN(u.StrParam5), MIN(u.ID) FROM Table1 AS a 
INNER JOIN Users AS u ON a.UserID=u.ID
WHERE " & WhereString1 & " AND (a.Date1 BETWEEN @datefrom AND @dateto) AND (u.Removed IS NULL) 
GROUP BY a.StrParam1, a.StrParam2
ORDER BY SUM(a.IntParam1) DESC;


Вот тут WhereString1 мы заменяем на один из трех вариантов:

Если выборка по аккаунту:
u.AccountID=0


Если выборка по списку ID пользователей:
u.ID IN (1,2,3,4,5,6...)


Если выборка по списку групп:
u.GroupID IN (11,12,13,14,15...)
Отредактировано 24.09.2024 13:28 dmitry251 . Предыдущая версия .
Re: mssql->entity framework
От: BlackEric http://black-eric.lj.ru
Дата: 24.09.24 19:38
Оценка: +1
Здравствуйте, dmitry251, Вы писали:

D>Всем привет.

D>переписываю тут проект на EF. Столкнулся с проблемой, которую никак не могу придумать как решить.
D>Есть запрос, в который в WHERE подставляется условие в зависимости от того, хотим ли мы сделать выборку по аккаунту, или по группе пользователей или по пользователю.
D>В примере который я приведу ниже, этот запрос обращается к таблице Table1, но таких таблиц около 30 и каждый раз запрос разный, ответ БД (структура) разные, но условие WHERE одно и то же.
D>Сейчас в программе в каждом контроллере идет вызов функции GetWhere, которая в зависимости от параметров формирует строку, которая подставляется в один из 30+ запросов (их значительно больше 30, думаю что не меньше 200).
D>И я не знаю как это переписать универсально, с тем чтобы осталось какое то подобие GetWhere, одинаково подходящее к всем переписанным запросам.

В общем-то ничего лучше чем это видеть не приходилось: Build Where Clause Dynamically in Linq.
Обычно в отдельном файле делается статический класс с набором методов WhereBy... или же один длинный метод с большим switch внутри.
https://github.com/BlackEric001
Re[2]: mssql->entity framework
От: dmitry251  
Дата: 25.09.24 11:34
Оценка:
Здравствуйте, BlackEric, Вы писали:

BE>Здравствуйте, dmitry251, Вы писали:


D>>Всем привет.

D>>переписываю тут проект на EF. Столкнулся с проблемой, которую никак не могу придумать как решить.
D>>Есть запрос, в который в WHERE подставляется условие в зависимости от того, хотим ли мы сделать выборку по аккаунту, или по группе пользователей или по пользователю.
D>>В примере который я приведу ниже, этот запрос обращается к таблице Table1, но таких таблиц около 30 и каждый раз запрос разный, ответ БД (структура) разные, но условие WHERE одно и то же.
D>>Сейчас в программе в каждом контроллере идет вызов функции GetWhere, которая в зависимости от параметров формирует строку, которая подставляется в один из 30+ запросов (их значительно больше 30, думаю что не меньше 200).
D>>И я не знаю как это переписать универсально, с тем чтобы осталось какое то подобие GetWhere, одинаково подходящее к всем переписанным запросам.

BE>В общем-то ничего лучше чем это видеть не приходилось: Build Where Clause Dynamically in Linq.

BE>Обычно в отдельном файле делается статический класс с набором методов WhereBy... или же один длинный метод с большим switch внутри.


Да, это неплохо, только классов у меня под сотню. Для каждого добавлять абсолютно одинаковый фильтр не комифильфо.
Re[3]: mssql->entity framework
От: Qulac Россия  
Дата: 25.09.24 16:48
Оценка:
Здравствуйте, dmitry251, Вы писали:

D>Здравствуйте, BlackEric, Вы писали:


BE>>Здравствуйте, dmitry251, Вы писали:


D>>>Всем привет.

D>>>переписываю тут проект на EF. Столкнулся с проблемой, которую никак не могу придумать как решить.
D>>>Есть запрос, в который в WHERE подставляется условие в зависимости от того, хотим ли мы сделать выборку по аккаунту, или по группе пользователей или по пользователю.
D>>>В примере который я приведу ниже, этот запрос обращается к таблице Table1, но таких таблиц около 30 и каждый раз запрос разный, ответ БД (структура) разные, но условие WHERE одно и то же.
D>>>Сейчас в программе в каждом контроллере идет вызов функции GetWhere, которая в зависимости от параметров формирует строку, которая подставляется в один из 30+ запросов (их значительно больше 30, думаю что не меньше 200).
D>>>И я не знаю как это переписать универсально, с тем чтобы осталось какое то подобие GetWhere, одинаково подходящее к всем переписанным запросам.

BE>>В общем-то ничего лучше чем это видеть не приходилось: Build Where Clause Dynamically in Linq.

BE>>Обычно в отдельном файле делается статический класс с набором методов WhereBy... или же один длинный метод с большим switch внутри.


D>Да, это неплохо, только классов у меня под сотню. Для каждого добавлять абсолютно одинаковый фильтр не комифильфо.


А нельзя тут будет рефлексию задействовать, например пометить классы атрибутом и используя это генерить expression tree ?
Программа – это мысли спрессованные в код
Re[3]: mssql->entity framework
От: RushDevion Россия  
Дата: 26.09.24 15:02
Оценка:
D>Да, это неплохо, только классов у меня под сотню. Для каждого добавлять абсолютно одинаковый фильтр не комифильфо.

А в этих классах фильтруемые свойства одинаково называются (т.е. AccountId, GroupId, UserId) или везде по-разному?
Если одинаково, то можно вот так попробовать:
interface IFilterCriteria {
  long? AccountId { get; }
  long UIDList[] { get; }
  long GroupIdList[] { get; }
}

public IQueryable<T> ApplyFilter<T>(this IQueryable<T> query, IFilterCriteria criteria) {

  if (filter.AccountId.HasValue) {
    Expression<Func<T,bool>> filterExp = BuildAccountIdExpression<T>(criteria.AccountId.Value); // TODO: построить filter expression в предположение, что у объекта T есть свойство AccountId
    return query.Where(filterExp);
  }
  
  if (filter.UIDList.Count > 0) {
    Expression<Func<T,bool>> filterExp = BuildUIDListExpression<T>(criteria.UIDList);  // TODO: построить filter expression в предположение, что у объекта T есть свойство UserId
    return query.Where(filterExp);
  }
  
  if (filter.GroupId.Count > 0) {
    Expression<Func<T,bool>> filterExp = BuildGroupIdListExpression<T>(criteria.GroupIdList); // TODO: построить filter expression в предположение, что у объекта T есть свойство GroupId
    return query.Where(filterExp);
  }

  return query;
}
Re[4]: mssql->entity framework
От: pilgrim_ Россия  
Дата: 26.09.24 15:16
Оценка: +1
Здравствуйте, RushDevion, Вы писали:

RD>А в этих классах фильтруемые свойства одинаково называются (т.е. AccountId, GroupId, UserId) или везде по-разному?

RD>Если одинаково, то можно вот так попробовать:

Если одинаково, то можно упростить и обойтись без expressions:

interface IFilterCriteria {
  long? AccountId { get; }
  long[] UIDList { get; }
  long[] GroupIdList { get; }
}

interface IFilterable
{
  long AccountId { get; }
  long UserId { get; }
  long GroupId { get; }
}

public IQueryable<T> ApplyFilter<T>(this IQueryable<T> query, IFilterCriteria criteria) 
where T : IFilterable
{

  if (filter.AccountId.HasValue) {
    query = query.Where(o => o.AccountId == criteria.AccountId);
  }
  else if (filter.UIDList.Count > 0) {
    query = query.Where(o => criteria.UIDList.Contains(o.UserId));
  }
  else if (filter.GroupIdList.Count > 0) {
    query = query.Where(o => criteria.GroupIdList.Contains(o.GroupId));
  }

  return query;
}
Re[3]: mssql->entity framework
От: Sinclair Россия https://github.com/evilguest/
Дата: 28.09.24 11:03
Оценка:
Здравствуйте, dmitry251, Вы писали:

D>Да, это неплохо, только классов у меня под сотню. Для каждого добавлять абсолютно одинаковый фильтр не комифильфо.

То есть в каждом из этих классов есть UserID, верно?
Тогда можно попробовать сделать так:
1. Объявляем общий интерфейс и реализуем его во всех нужных классах:
public interface IUserBound
{
  public string UserID {get; set;}
}

2. Делаем хелпер-класс:
public static class UserFilters
{
  public static IQueryable<T> FilterByAccountId(this IQueryable<T> query, int accountId)
    where T: IUserBound
     => from q in query
        from u in db.Users
        where q.UserID = u.ID && u.AccountID == accountId 
        select q;

  public static IQueryable<T> FilterByUsers(this IQueryable<T> query, params int[] userIds)
    where T: IUserBound
     => from q in query
        where userIds.Contains(q.UserId)
        select q;

  public static IQueryable<T> FilterByUserGroups(this IQueryable<T> query, params int[] groupIds)
    where T: IUserBound
     => from q in query
        from u in db.Users
        where q.UserID = u.ID && groupIds.Contains(u.GroupID)
        select q;
}

3. В контроллерах, где нужно, вызываем соответствующие методы, заменяя from db.SomeObjects на from db.SomeObjects.FilterByUsers(userList) и так далее.
Если у вас там везде нужно делать одинаковое ветвление вида "если есть аккаунт ID — то по нему, иначе — по списку пользователей" и прочее, то можно всё это завернуть в 1 функцию, как предложил коллега тут.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.