IQuerable: на пороге велосипеда
От: LWhisper  
Дата: 14.09.17 22:13
Оценка:
Привет!
Вместо тысячи слов:

Есть локальная база, есть серверная.
Есть запрос FindEntryById, который может отправляться к любой из них.
В случае серверной базы, возвращаются только те записи, которые ассоциированы с пользователем, под которым осуществляется подключение к серверу. Перед возвратом модифицируются.
Все запросы к базе выполняются нативными SQL-квирями без ORM.

Запросов тысячи.
Пользователей тысячи.
Нужна обратная совместимость между версиями.

Я стою на пороге написания собственного IQuerable, который будет по разному транслировать запросы на стороне клиента и сервера, оттопыривая наружу единый API.
Меня беспокоит тот факт, что придется повторить путь Linq2Db с моделью CodeFirst, собрать все возможные грабли и реализовать не самый простой интерфейс с разбором экспрешшенов.
Есть ли пути проще?

Также буду рад если вы поделитесь чем-нибудь помимо примеров с MSDN касательно IQuerable и best practice по написанию подобных велосипедов.
iquerable sql orm repository .net c#
Re: IQuerable: на пороге велосипеда
От: IT Россия linq2db.com
Дата: 15.09.17 00:07
Оценка:
Здравствуйте, LWhisper, Вы писали:

LW>Я стою на пороге написания собственного IQuerable, который будет по разному транслировать запросы на стороне клиента и сервера, оттопыривая наружу единый API.

LW>Меня беспокоит тот факт, что придется повторить путь Linq2Db с моделью CodeFirst, собрать все возможные грабли и реализовать не самый простой интерфейс с разбором экспрешшенов.
LW>Есть ли пути проще?

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

Что касается общего API, то здесь вообще не вижу проблем.

List<Boogie> GetBoogieList(int? userID)
{
    using (var db = new BoogieWoogieDataContext())
    {
        var q =
            from b in  db.Boogie
            ....
            ;

        if (userID.HasValue)
        {
            q =
                from t in q
                join u in db.Users on t.UserID equals u.UserID
                where u.UserID == userID && u.XXX == YYY
                select t;
        }

        return q;
}

как-то так.

К тому же, в шаблоне генерации модели данных ко всем таблицам, в которых есть поле, например, UserID, можно прилепить интерфейс IUserable и тогда вторую часть вышеобозначенного метода можно оформить в виде следующего метода:

IQueriable<T> ApplyUser<T>(IQueriable<T> q, int? userID)
    where T : IUserable
{
    if (userID.HasValue)
        return
            from t in q
                join u in db.Users on t.UserID equals u.UserID
                where u.UserID == userID && u.XXX == YYY
                select t;

     return q;
}

ЗЫ. Код условный и рассчитан на творческую переработку.
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: IQuerable: на пороге велосипеда
От: LWhisper  
Дата: 15.09.17 07:39
Оценка:
Здравствуйте, IT, Вы писали:

IT>Что касается общего API, то здесь вообще не вижу проблем.


Вопрос не в том, как эти запросы строить на стороне клиента.
Вопрос в том, как их транслировать между клиентом-базой, клиентом-сервером и сервером-базой.

Если где-то userId не будет использоваться, то соответственно, и тащить его смысла нет. В нужном месте достаточно дополнить условие, как в твоём примере с IUserable. И раз уж мне нужно место, в которое этот запрос будет приходить на стороне сервера под определенной сессией пользователя, то нет смысла его явно пробрасывать в этот метод, достаточно взять из сессии.
Re: IQuerable: на пороге велосипеда
От: Danchik Украина  
Дата: 15.09.17 08:53
Оценка: 4 (1)
Здравствуйте, LWhisper, Вы писали:

[Skip]

LW>Я стою на пороге написания собственного IQuerable, который будет по разному транслировать запросы на стороне клиента и сервера, оттопыривая наружу единый API.

LW>Меня беспокоит тот факт, что придется повторить путь Linq2Db с моделью CodeFirst, собрать все возможные грабли и реализовать не самый простой интерфейс с разбором экспрешшенов.
LW>Есть ли пути проще?

CodeFirst? Самописный есть?

LW>Также буду рад если вы поделитесь чем-нибудь помимо примеров с MSDN касательно IQuerable и best practice по написанию подобных велосипедов.


Пару вариантов:

  1. В linq2db есть Wcf Linq Servive. Может на его основе сие замутить? Пока он умет Linq запрос прокидывать на сервер, там строить SQL и возвращать назад результат.
  2. IQueryable прекрасно разбирается через Relinq. Он строит свой AST на экспрешинах и разбирать его па порядки проще. самплы
    Пропарсать просто можешь парой строчек:
    IQueryable<Entity> entities = ....
    var parser = QueryParser.CreateDefault();
    var ast    = parser.ExpressionTreeParser.ParseTree(entities.Expression);

    Там уже в дебаггере разбираешься что же он тебе напарсил. Там все красиво по полочкам разложено. Только вот сомневаюсь что ты сможешь сие дело сериализировать, наверняка придется трансформировать их AST в свой.
Отредактировано 15.09.2017 9:39 Danchik . Предыдущая версия . Еще …
Отредактировано 15.09.2017 9:00 Danchik . Предыдущая версия .
Отредактировано 15.09.2017 8:54 Danchik . Предыдущая версия .
Re: IQuerable: на пороге велосипеда
От: vorona  
Дата: 18.09.17 07:50
Оценка:
Здравствуйте, LWhisper, Вы писали:

Если использовать Ef core, то для дополнительной фильтрации можно использовать Model-level query filters.

Для связи клиента с сервером можно использовать OData. На стороне клиента OData Client на стороне сервера мой велосипед OdataToEntity.
Re[2]: IQuerable: на пороге велосипеда
От: Danchik Украина  
Дата: 18.09.17 10:24
Оценка:
Здравствуйте, vorona, Вы писали:

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


V>Если использовать Ef core, то для дополнительной фильтрации можно использовать Model-level query filters.


V>Для связи клиента с сервером можно использовать OData. На стороне клиента OData Client на стороне сервера мой велосипед OdataToEntity.


OData ой как ограничена. Джоинов там точно нет. О сложных запросах совсем молчу.
Я бы все-таки покурил в сторону как экстенднуть linq2db WCF Linq Service, сам подумывал как бы там хуки на фильтрацию вставить, может и дойдут руки. Это по затратам намного меньше того, что придется велосипедировать играясь с IQueryable.

Должна получится такая архитектура



Как видим нужно написать только TableFilterHook — здесь будем фильтровать рекорды так чтобы лишнее в запрос не попало.
Можно подумать как такое сделать малой кровью.
Re[3]: IQuerable: на пороге велосипеда
От: vorona  
Дата: 18.09.17 13:08
Оценка:
Здравствуйте, Danchik, Вы писали:

D>OData ой как ограничена. Джоинов там точно нет. О сложных запросах совсем молчу.


Join описываются через $expand и $select navigation properties
Re[4]: IQuerable: на пороге велосипеда
От: Danchik Украина  
Дата: 18.09.17 14:33
Оценка:
Здравствуйте, vorona, Вы писали:

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


D>>OData ой как ограничена. Джоинов там точно нет. О сложных запросах совсем молчу.


V>Join описываются через $expand и $select navigation properties


Да видел, не вникал, но как мне кажется, здесь без метаданных далеко не уедешь. Хотя и это решение имеет право на жизнь.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.