Дано: система с 10-летней историей, разработанная на C#, с довольно большой кодовой базой. Серверной частью системы является веб-сервис (хостится в IIS, протокол SOAP), активно работающий с базой данных, с кэшированием в Redis, с различными проверками безопасности, поиск в Elasticsearch.

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

Напрашиваются 3 варианта решения задачи:

  1. Использовать Mono
  2. Использовать .NET Core
  3. Переписать все на Java\Go\Python

3-й вариант отбрасывается сразу же, на переписывание уйдет несколько человеко-лет разработки. Остаются Mono и .NET Core.

Что не так с Mono


Хоть и технология появилась давно и успешно используется (например, в Xamarin) есть несколько сомнений насчет дальнейшего развития Mono:

  • Приобретение Xamarin Microsoft'ом. Microsoft публично делает ставку на .NET Core и вкладывает в него деньги. Процесс разработки .NET Core открыт, доступны даты релизов, roadmap и т.д. Что будет с Mono вне Xamarin вопрос. Будет ли Microsoft одновременно развивать 2 конкурирующие реализации .NET?

  • Отсутствие фидбека. С какой производительностью работает Mono? Можно ли применять в enterprise? Про Mono почти не говорят на конференциях, такое чувство, что технологию никто не использует

В итоге выбор пал на .NET Core. Задача усложняется, что нужно одновременно поддерживать и .NET 4.6.1 реализацию, и .NET Core в одной кодовой базе, так как не всех клиентов можно обновить и нужна обратная совместимость.

Миграция проектных файлов


Для начала нужно установить Update 3 для Visual Studio 2015 и свежий .NET Core SDK (свежие билды публикуются на странице проекта). Затем для каждого csproj-файла нужно создать его аналог — project.json. Заходим в папку с каждый проектом, в командной строке вводим dotnet new.

Появится project.json, из него нужно удалить строку про emitEntryPoint, если сборка не содержит метода Main. Далее, создаем пустой sln-файл, добавляем в него project.json файлы, как проекты. В каждый project.json следует прописать зависимости на проекты (раздел dependencies), пробуем скомпилировать. Если ошибок компиляции нет — поздравляю. Единственная проблема, что старый солюшен с csproj-проектами перестанет работать, чтобы оба проекта работали side by side есть решение. Выглядит довольно странно, но работает.

Что не будет работать


В нашем приложении столкнулись со следующими проблемами:

  • app.config — Разработчики corefx отказались от устаревшего API работы с конфигурационными файлами. Теперь конфигурации рекомендуется хранить в json файле.
  • PerformanceCounter — Windows специфика, для использования не на Windows платформе следует искать какую-то альтернативу.
  • DataSet, DataTable — считается устаревшими API.
  • LCID у CultureInfo — у культуры теперь нет свойства LCID, CultureInfo теперь можно создать только по имени.
  • Свойство Assembly у экземпляра Type — чтобы получить описание сборки у типа необходимо использовать метод-расширение GetTypeInfo(). Ломает совместимость с текущим кодом, особенно в ресурсных файлах.
  • MachineKey — Windows специфика, нет возможности получить уникальный идентификатор текущей машины.
  • Thread.SetData, Thread.GetData — добавят во второй версии стандарта.

Это все, что касается API стандартной библиотеки. Очевидно, следует перерабатывать код использующий нативные вызовы (в нашем случае код проверки прав доступа и работа с дескрипторами безопасности). Файлы с таким legacy-кодом исключали из компиляции (раздел exclude в project.json) или отключали директивой условной компиляции (NETCOREAPP1_0).

SOAP


В текущей реализации .NET Core нет способа из коробки создать Web-сервис работающий по SOAP. Есть пример с MSDN, где показывают как можно при особом желании поддержать SOAP протокол. Мы для себя решили отказаться от SOAP и перевести веб-сервис на REST. Никаких проблем нет, ASP.NET Core не отличается от ASP.NET Web API. Есть DI с которым можно жить. Контроллеры, роуты, даже swagger работает — все на месте.

Остались еще большие проблемы: аутентификация пользователей (раньше это делал IIS) и какую библиотеку использовать для управления правами доступа, интеграции с LDAP.

Первую проблему можно решить поставив перед нашим приложением Apache с необходимым расширением и включив reverse proxy. Библиотеку для управления ACL еще не нашли.

Выводы


Visual Studio 2015 Update 3 и Resharper на проектах .NET Core работают идеально, с отладкой и скоростью компиляции проблем нет. Наконец-то для доставки приложения можно использовать Docker. Unit-тесты работают.

До полной уверенности нужно проводить нагрузочные тесты. Но в целом технология имеет право на жизнь, хоть и есть проблемы с тулингом: например, не поддерживается msbuild. Рекомендую .NET разработчикам присмотреться и начать использовать .NET Core в своих проектах. Для нас пока опыт только положительный.
Поделиться с друзьями
-->

Комментарии (32)


  1. Sterk
    11.10.2016 12:22
    +3

    Что в конечном итоге с производительностью?


    1. kotov_a
      11.10.2016 12:32
      +1

      Полноценное нагрузочное тестирование еще не проводили. Методы в духе: получить запрос по REST, сходить в базу (MS SQL, PosgreSQL), сохранить результат в Redis, отдать ответ клиенту — работают гарантированно не медленней, аналогичной реализации в .NET 4.6.1.


  1. Whity314
    11.10.2016 12:32
    -1

    Спасибо. Позновательно.


  1. andreycha
    11.10.2016 12:49
    +11

    Мало.


  1. Nagg
    11.10.2016 13:36
    +5

    Про Mono почти не говорят на конференциях, такое чувство, что технологию никто не использует

    Кроме Unity и Xamarin приложений ;-)


    1. HolyCode
      12.10.2016 11:40

      На прошедшем в конце мая DevCon'16 пара евангелистов и премьер филды сказали, цитирую: в головном говорят, что пока Моно активно не PR'им ибо он не Cloud-Ready.


  1. Hydro
    11.10.2016 14:10

    А проект действительно крупный?
    Меня недавно от такого шага остановило то, что в .NET Core по бороде пошел Reflection API, который я активно использовал


    1. kotov_a
      11.10.2016 14:51

      Сейчас снял метрики (в количестве строк кода):

      • 17.5к строк логика обработки данных
      • 31.5к строк вызова хранимых процедур (по большей части сгенерированный код)
      • 17к строк описание моделей, интерфейсов


      Всего сборок 14.


      1. hameleon86
        11.10.2016 22:09
        +2

        Ну это совсем не крупный проект


        1. kotov_a
          11.10.2016 22:16

          Однозначно не самый большой проект на C# в индустрии, но кодовой базы вполне достаточно, чтобы сделать выводы о пригодности технологии и собрать критичные грабли.


  1. Taritsyn
    11.10.2016 14:26
    +1

    Единственная проблема, что старый солюшен с csproj-проектами перестанет работать, чтобы оба проекта работали side by side есть решение. Выглядит довольно странно, но работает.

    Чтобы решить эту проблему я разделил проект на 2 директории: ИМЯ_ПРОЕКТА и ИМЯ_ПРОЕКТА.Net4. В первой директории оставил: xproj-файл, project.json, весь общий код, а также код, необходимый для .NET Core-версии. Во второй: csproj-файл, packages.config и код, необходимый для .NET Framework 4.0-версии (файлы с общим кодом добавляются в csproj-файл в виде ссылок).


  1. force
    11.10.2016 14:39

    С этим .NET Core иногда нужно столько переписывать, что пункт 3 из вашего списка не кажется таким уж проблемным. То, что я увидел в .NET Core:
    1. Полностью переделана конфигурация. Т.е. сегодня модно в JSON, завтра в protobuf будет. Придётся переписывать весь конфиг для проектов.
    2. Очень сильно перекочеврыжен рефлекшен. В большинстве случаев решается через GetTypeInfo (обезьянкой ходишь по коду и вставляешь эту строчку). Опять же, переписывать кучу всего.
    3. Потеря кучи библиотек, или сидеть ждать, когда авторы переделают, или пытаться самостоятельно, если исходники есть. Банальный log4net не работает
    4. Постоянная смена API. Например, Kestrel с каждой версией полностью меняет API. При этом «каждая» версия это (1.0.0-beta, 1.0.0-rc, 1.0.0). До кучи бардак в репозиториях — тот же кестрел лежит под тремя именами.
    5. Периодически мелкое изменение API стандартных библиотек в самых неожиданных местах. Например у RSA нельзя получить XML с параметрами. Т.е. весь код надо обкладывать ифами и проводить тестирование

    При этом по факту, под mono работает то, что и вроде бы и не должно работать, например HttpListener. Скорость — не ясно, но числодробилки на .NET реализовывать странно, а в бизнес.приложениях всё равно всё упирается в базу данных, или как у вас в SOAP, который архитектурно никогда быстрым не был.


    1. kotov_a
      11.10.2016 15:19

      По пунткам:

      1. Верно, но на фоне объема нашего приложение сложность чтения настроек слишком мала, можно пренебречь.
      2. Согласен полностью, мартышкин труд.
      3. Мы используем: Newtonsoft.Json, StackExchange.Redis.StrongName, Npgsql. Такая проблема действительно есть, сам жду когда спортируют мой любимый MiniProfiler. NLog не используете?
      4. Именно поэтому ждал RTM, чтобы не вникать в эти мелкие изменения. Сейчас такой проблемы не заметил, на версию 1.0.1 обновились без сложностей.
      5. Все верно, так и поступили.


      Это и пугает, фидбека о технологии очень мало, неизвестно, что ожидать. Проблемы с базой действительно возникают (поэтому используем Redis), но хотелось бы, чтобы рантайм языка программирования (как и стандартная библиотеки) не подводили и работали быстро.


      1. force
        11.10.2016 15:33

        3. Исторически сложился log4net — особого смысла менять, пока не видим.

        Пока, имхо, стоит ждать .NET Core 2.0, смотреть что у них получится, заодно фидбека и best practices накопится. С Mono, если удастся подобрать идентичное железо попробую натравить свою числодробилку, оптимизированную под .NET, на Mono и .NET Core, посмотрю на результаты. Если не забуду, отпишусь :)


  1. Taritsyn
    11.10.2016 14:53
    +3

    Свойство Assembly у экземпляра Type — чтобы получить описание сборки у типа необходимо использовать метод-расширение GetTypeInfo(). Ломает совместимость с текущим кодом, особенно в ресурсных файлах.

    Это является проблемой только тогда, когда нужно поддерживать 2 версии проекта (для .NET Core и .NET Framework 4.0).

    Чтобы сгладить различия между разными реализациями Reflection, я использую полифиллы (например, TypeInfo.cs и TypeExtensions.cs).

    С генерацией ресурсных файлов сложнее. Пришлось отказаться от стандартных генераторов в пользу собственного, написанного под Sake и KoreBuild.


    1. ForNeVeR
      11.10.2016 15:18

      Воу, вот за генератор ресурсов спасибо.


      Эх, а он у вас тоже только строковые ресурсы поддерживает, да? Никаких там ImageList в него не запаковать? :(


      1. Taritsyn
        11.10.2016 16:33

        Да, только строковые ресурсы. За основу брал код стандартного генератора от Microsoft.


  1. mayorovp
    11.10.2016 14:54

    По поводу SOAP немного не понял. Что именно не работает в .NET Core: HttpBinding вообще или всего лишь шаринг порта + активация?


    1. kotov_a
      11.10.2016 15:27

      Нет веб-сервисов как таковых (WCF, aspx, svc и так далее). Соответственно, байндингов тоже нет. Есть ASP.NET Core, который из коробки поддерживает только REST.


      1. mayorovp
        11.10.2016 15:29

        Там в примере с msdn есть строчка:


         var encoder = binding.CreateBindingElements().Find<MessageEncodingBindingElement>()?.CreateMessageEncoderFactory().Encoder;

        Значит, как минимум библиотеки WCF доступны.


        1. kotov_a
          11.10.2016 15:36

          Полагаю, строчка относится к .NET 4.x. В .NET Core есть поддержка клиентского WCF и есть всеобщий молебен с просьбами поддержать серверный. Там же есть несколько ссылок на проекты, которые в какой-то мере могут заменить WCF.


          1. mayorovp
            11.10.2016 15:38
            -1

            Эта строчка относится к .NET Core, потому что является частью примера по запуску WCF-службы на ASP.NET Core.


            1. dotnetdonik
              12.10.2016 11:34

              Если запустить ASP.NET Core под 4.6, тогда и service model будет доступна. Собрать проект, который референсит service model как netcoreapp на текущий момент нельзя, насколько помниться.


      1. mayorovp
        11.10.2016 15:41

        Немного пояснений по svc-файлам. svc-файл — это не веб-сервис как таковой, а механизм его активации. Когда запрос попадает на svc-файл, обработчик svc-файлов конструирует и настраивает ServiceHost, после чего соответствующий URL перестает обслуживаться IIS и начинает обслуживаться ServiceHost.


        После этого те запросы, которые пришли на svc-файл в IIS, тихонько "подкладываются" во входную очередь http-транспорта.


  1. Indermove
    11.10.2016 15:20

    А как много времени на это все ушло? Были ли какие-то ошибки компиляции, которые нужно было долго исправлять?


    1. kotov_a
      11.10.2016 15:22

      На миграцию проектных файлов и первую компиляцию ушло почти 3 дня одного разработчика. Нет, с такими ошибками не сталкивались.


  1. k0st1x
    12.10.2016 11:22

    > Что будет с Mono вне Xamarin вопрос. Будет ли Microsoft одновременно развивать 2 конкурирующие реализации .NET?

    у Mono Project есть публичные доски в трелло для слежки за процессом разработки, оно же и является roadmap'ом
    https://trello.com/monoproject1


  1. HolyCode
    12.10.2016 11:23

    Хороший вектор на переход к Core. По поводу аутентификации была такая же проблема, находил статейку где учётки и права можно с IIS портировать в базейку, а её уже юзать под Core Identity. Я конечно не вижу всей Вашей картины, но покапать можно туда.


  1. xeon
    12.10.2016 15:25
    +1

    Удивляет, что не упомянуты проблемы сторонних библиотек. У нас в солюшене 100+ проектов и десятки nuget пакетов и сторонних библиотек, далеко не все из которых имеют поддержку .net core и в принципе могут работать на Linux, даже под mono. Не очень ясно, когда можно будет взлетать со всем этим хозяйством.


  1. caballero
    12.10.2016 16:06
    -3

    переход на core вряд ли сейчас оправдан — майкрософт может еше не один раз перевернуть там все с ног на голову как недавно.
    Лично я не вижу пока особого смысла запускать .NET проекты под линуксом. Обычно если разраб выбирает .NET то он выбирает и остальной стек майкрософта начиная с самой платформы и заканчивая mssql например. Опять же множество библиотек и наработок которые не будут выполнятся под линуксом пока не будут портированы.
    ИМХО пока основная фишка core — это опенсорс. Но с таким же успехом можно писать приложения на яве — тоже как правило опенсорс и кросплатформеность, Не говоря про количество библиотек несравнимое со всем дотнетом вместе взятым.
    .

    .


  1. 323066
    18.10.2016 10:39

    Где-то с полгода назад мы пробовали перевести крупный .NET проект с 12-летней историей на Mono. В связи с плавным переходом на микросервисную архитекруту, просто завораживала возможность ранать и деплоить все единообразно(docker, kubernetes, все дела).
    За несколько человеко-недель удалось собрать проект под моно и поправить невероятно странные ошибки, которые возникали из-за отличий работы неоторых библиотек на .NET и Mono(в частности ReLinq просто собирал разные expression trees под обеими платформами). Потом запустили перформанс тесты, которые у нас есть в неплохом количестве. Результаты оказались удручающими. Некоторые сервисы просели в разы. У нас в проекте много бизнес-логики, но также очень много работы с деревьями выражений, свой QueryProvider. Пытались разобраться какие места конкретно тормозят, но оказалось не так-то просто. Профайлер под моно грустный(как и monodevelop). Пытались вырезать из конкретного сервиса логику, до тех пор пока не доберемся до места где происходит различие, но не особо получилось. Кажется, что рефлексия, работа со строками, сборка мусора под mono проседает, но 100% до конца не разобрались. В общем провал.
    Ждем нативного докера под windows.


    1. Hydro
      18.10.2016 13:27

      Сочувствую. Чуть не заплакал, когда прочитал это сообщение.