Как стать автором
Обновить

Комментарии 18

Ого, наконец-то годная статейка.


Надо полагать, автор заодно захочет похвастаться знанием, как переопределить штатный QueryProvider у старого доброго Linq-2-Sql? Чтобы это добро все у меня заработало из коробки.

Я не думаю, что можно переопределить штатный провайдер, вы можете написать обертку для IQueryable которая при вызове ToList/First/Count/… будет переписывать оригинальный Expression, и в этой обертке добавлять часть общих бизнес правил, если тема представляет интерес, сделаю статью, потому что в реализации есть подводные камни

пожалуйста, будьте добры.

Для тех, кто не совсем в теме, можете пояснить что значит «не справляется»? Выдает неправильный результат? В общем, было бы здорово увидеть что ожидается и что получается. И зачем там проверка на пустую строку?

штатный провайдер просто не умеет конвертировать вызовы к Format в нужную форму для обращения к БД

Он не может понять, что нужно выбрать всего два поля и вместо этого выбирает все поля? Или просто ничего не выбирает? А зачем проверка на пустую строку?

он в принципе не умеет работать с string.Format. а это именно то, во что разворачивается интерполирование.

Я так и не понял что будет, если оставить пример как есть, ошибка рантайма, полностью неверный запрос в базу или верный, но просто неоптимальный.
Будет ошибка рантайма — EF (да и весь IQueryable) вобще капризный зверек когда дело касается вызова внешних методов в лямбдах (даже если они часть дотнета).

Одна из задач Queryable Provider'a транслировать запрос из Expression Tree(который строится, когда вы пишете LINQ к интерфейсу IQueryable) в SQL и делать запрос к базе данных.

Но тут есть проблемы:
1) Не весь LINQ будет транслирован в Expression Tree, например такой не будет:

  context.Select(x = > {
     if(x.Age < 18)
        return "Underage";
     else
       return "Adult";
   }

2) LINQ который удастся транслировать в Expression Tree, может быть не транслируем в SQL, в таких случаях EF Core 2.1 сделает запрос к базе данных с тем SQL запросом, который смог транслировать, а остальное обработает в памяти, но это поведение можно переопределить и он станет падать каждый раз, когда не может полностью разобрать Expression Tree.

Кстати ReSharper выделяет самые очевидные «проблемные» места запроса.
x => x != ""

Серьёзно?
а как же
string.IsNullOrEmpty(x)

?

Ваше решение подвержено проблеме с методом Include:


_context.Humans
  .Select(x => $"Name: {x.Name}  Age: {x.Age}")
  .Include(x => x.Foo) // Теперь выражение, переданное в Select, спрятано внутри константы
  .ReWrite() // И ReWrite его не видит

При этом решение этой проблемы осложнено тем, что каждый DbSet в EF — это ConstantExpression ссылающийся сам на себя. Решается эта проблема как-то так:


    private readonly HashSet<IQueryable> visiting = new HashSet<IQueryable>();

    protected override Expression VisitConstant(ConstantExpression node)
    {
        var value = node.Value as IQueryable;
        if (value == null) return node;

        if (!visiting.Add(value)) return node;
        try
        {
            var expr = value.Expression;
            var newExpr = Visit(expr);
            if (expr == newExpr) return node;

            var newValue = value.Provider.CreateQuery(newExpr);
            return Expression.Constant(newValue, node.Type);
        }
        finally
        {
            visiting.Remove(value);
        }
    }

Позанудствую… не помешала бы пара строк в начале, о том для кого статья и что мы из нее узнаем. Да, есть теги, и секунд за 20 понимаешь, что это про C#, которого ты в глаза никогда не видел, но все же..

А вы не пробовали «решить» эту проблему, отправив пулл реквест с фиксом в рамках .net core?
Нет, хорошая идея
При использовании экстеншена ReWrite к IQueryable

string human = _context.Humans
    .Select(x => $"Name: {x.Name}  Age: {x.Age}")
    .ReWrite()
    .First();


в полученной строке присутствуют лишние кавычки " в начале строки.
Спасибо, поправил
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории