Тонкости Lambda Expressions в C#

    То, о чем написано в статье, я отлавливал около 10 часов, это были 10 часов непрерывного дебага, которые cвелись к пошаговому сравнению рабочей и нерабочей версий кода, даже не так, к сравнению каждой строчки из окошка дебага рабочей и не рабочей версий кода


    Для программиста, не знакомого с деревьями выражений хорошая вводная статья на msdn, в ней подробно раскрываются некоторые шаги в построении дерева, которые я опустил в статье.


    В каком случае этот тест будет зеленым (включена надстройка EF, при которой он падает, если не может полностью транслировать запрос в SQL).



    • Во-первых, должен быть определен маппинг поля PupilName, если его не определить Query Provider не узнает, на что нужно спроецировать свойство PupilName, чтобы дописать в SQL ORDER BY.
    • Во-вторых, провайдер должен разобрать этот маппинг, вот, такой, например, не пойдет, EF начнет ругаться (по умолчанию не ругается, поднимает сущности в память — LINQ to Object, но это можно включить):




    А все дело в том, что Query Provider не понимает по какому свойству(свойствам) сущности мы хотим сортировать. Но если маппинг написан правильно, то провайдер справится, отправит SQL запрос, получит ответ и десериализует результаты.


    Иногда возникает необходимость написания Expression'ов самому, т.е что-то похожее:



    Я покажу, как написать этот Expression, это не так сложно, по ссылке выше приводятся несколько похожих примеров:



    Мы по шагам собираем лямбду, сначала параметр, свойство, а потом склеиваем все.
    В каком случае это не будет работать и тест будет зеленым(провайдер не сможет разобрать expression)?


    Не знаете? Не огорчайтесь, я тоже не знал, и в итоге потратил около 10 часов, чтобы понять.
    Вот подсказка, так все будет работать:



    PupilDto наследуется от PupilDtoBase


    Все дело в том, что PropertyInfo нужно брать из базового класса, если само свойство PupilName принадлежит базовому классу, так C# строит lambda expression, и EF разбирает их, полагаясь на стандарты языка


    Пруфы

    Что показывает debugger, если написать обычную лямбду в OrderBy:



    А в нашем случае так:



    AutoMapper и IncludeBase


    Если вы старайтесь не дублировать код, то наверняка сталкивались с наследованием DTO и IncludeBase:



    Бывает еще более изощренная ситуация:



    И этот тест будет зеленым:



    А дело в том же самом, снова PropertyInfo подтягивается из интерфейса:



    Пруфы

    ReflectedType свойства Age это интерфейс, IPupilDto, С# строит лямбду, в которой свойство Age — свойство класса PupilDto, а не интерфейса, а вот, как строит лямбду автомаппер:



    Как же решить эту проблему? Если IncludeBase Automapper'a с интерфейсом нас не устраивает ( если вы используете маппинг в памяти — это вас не коснется), то придется отказаться от этого API, я решил эту проблему выделив маппинг в extension метод, вот так:



    Тогда автомаппер сам найдет подходящее свойство типа по имени, построит "правильную" лямбду
    Спасибо за внимание!

    • +19
    • 8.3k
    • 6
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 6

      +11

      Это не тонкости Lambda Expressions, а тонкости Entity Framework. Причем подозрительно похожие на баги...

        0

        Это поведение c#, он так строит лямбду, так что ef поведение ef корректно

        +2
        В каком классе свойство — тот и используем, наследование придумали лентяи.
          0
          Если вы старайтесь не дублировать код, то наверняка сталкивались с наследованием DTO

          Наследование это не про "как не дублировать код", а про отношения между классами.

            0
            Т.е. статью можно заменить на одно предложение: строя PropertyExpression PropertyInfo нужно брать из базового класса? Я не из вредности, просто хочу понять есть еще что-то важное, на что автор хотел обратить внимание, а я упустил?
            • UFO just landed and posted this here

              Only users with full accounts can post comments. Log in, please.