Чек-лист перед миграцией с .NET Framework на .NET
· Го Комура · .NET, .NET Framework, C#, Модернизация, Разработка Windows, Миграция
Скачать Excel-чек-лист с листами на японском и английском языках
Поменять TargetFramework в .csproj на net10.0, обновить несколько пакетов NuGet, и если сборка прошла — всё готово.
…если бы миграция сводилась к этому, жизнь была бы гораздо спокойнее. На практике чаще получается иначе.
В реальных проектах на .NET Framework дремлет немало допущений, о которых обычно не задумываешься: System.Web, WCF, Web Forms, старый packages.config, web.config.install.xdt, нативные DLL, COM / ActiveX, сторонние компоненты, работающие только в дизайнере, неявные предположения о x86, ResX, завязанный на дизайнер, устаревшие сериализаторы и многое другое.
Поэтому по-настоящему важная часть миграции с .NET Framework на .NET — это инвентаризация до начала реализации. Если разложить вопросы по полочкам заранее, миграция превращается не в «большую ставку», а в «последовательную работу по вычёркиванию пунктов».
В этой статье собрано то, что стоит проверить перед миграцией существующего бизнес-приложения на .NET Framework 4.x на текущий .NET. Основные объекты рассмотрения — примерно такие:
- Библиотеки классов
- Консольные приложения
- Службы Windows
- WinForms / WPF
- ASP.NET Framework (MVC / Web API / Web Forms)
- Приложения, использующие WCF
- Приложения, использующие EF6
Статья написана по состоянию на 2026-03-15. Сроки поддержки и рекомендации официальных инструментов меняются, поэтому если вы читаете её значительно позже, сверьтесь также с официальной информацией.
1. Сначала — вывод
Сначала — только выводы, в которых сложно ошибиться.
- Сначала нужно навести порядок на стороне .NET Framework, до миграции. Официальное руководство Microsoft тоже рекомендует перед портированием перейти на .NET Framework 4.7.2 или новее, а также заранее выполнить переход на
PackageReference, перейти на формат SDK-стиля и обновить зависимости. - Сложность определяется скорее моделью приложения, чем объёмом кода. Библиотеки классов и консольные приложения относительно лёгкие; ASP.NET Framework, Web Forms, серверы WCF и WF обычно оказываются тяжёлыми.
- WinForms / WPF можно перевести на .NET, но они остаются Windows-специфичными. Ошибиться здесь — значит наступить на классическую мину: перенос выполнен, а приложение так и не запускается в Linux-контейнере.
- Переход ASP.NET Framework → ASP.NET Core фактически является миграцией архитектуры. Небольшие приложения иногда можно перевести одним махом, но для крупных производственных систем безопаснее заранее рассчитывать на поэтапную миграцию.
- WCF и EF6 иногда можно отделить от миграции runtime. У клиента WCF есть поддерживаемые пакеты для .NET, а EF6 можно мигрировать на EF Core отдельным этапом уже после перехода на современный .NET.
- С другой стороны, создание AppDomain, .NET Remoting, CAS, COM+, Workflow Foundation и зависимость от BinaryFormatter — это красные флаги. Не найти их заранее — значит получить взрыв трудозатрат позже.
packages.config/install.ps1/ XDT / активыcontent/ нативные DLL / COM / ActiveX / предположения о x86 легко проходят сборку, но падают во время выполнения или в дизайнере, поэтому их нужно выявить до начала работы.- По состоянию на 2026-03 LTS-версией является .NET 10. Для новой миграции естественно ориентироваться именно на текущую LTS.
- Миграция без подготовленных тестов, замеров и плана отката опасна. Миграция — это не столько работа по реализации, сколько работа по последовательному выявлению скрытых допущений.
2. Сначала решаем, действительно ли нужно мигрировать прямо сейчас
Первое, что нужно решить, — не «как мигрировать», а действительно ли это приложение нужно мигрировать именно сейчас.
Если оставить этот вопрос расплывчатым, легко прийти либо к технически правильной, но неподъёмной для бизнеса миграции, либо, наоборот, к чрезмерному откладыванию, хотя мигрировать явно стоило бы.
2.1 Решение остаться на .NET Framework — тоже нормальный вариант
.NET Framework 4.8.1 продолжает поддерживаться, пока работает на поддерживаемой версии Windows. То есть речь не о том, что если прямо сейчас не перевести всё на современный .NET, компания немедленно окажется в опасности.
Однако у решения остаться есть чёткие ограничения.
- невозможность выйти за пределы Windows
- продолжение поддержки ASP.NET Web Forms и устаревшего серверного стека
- трудно получить выгоду от улучшений производительности, языковых возможностей и экосистемы нового .NET
- растущее расхождение с современными предположениями об облаке, контейнерах и CI/CD
И наоборот, если у вас сильная зависимость от следующего, разумно пока стабильно эксплуатировать приложение на .NET Framework 4.8.1, параллельно выстраивая план замены на отдельном треке.
- огромный массив экранов на Web Forms
- необходимость строго сохранить совместимость сервера WCF
- глубокая зависимость от Workflow Foundation или COM+
- сторонние компоненты для дизайнера не поддерживают современный .NET
- бизнес не допускает крупных изменений в спецификации
2.2 Что меняется в зависимости от выбора
| Выбор | Что улучшается | Что остаётся / теряется | Подходящий случай |
|---|---|---|---|
| Остаться на .NET Framework 4.8.1 | Легко сохранить существующие активы и стабильно эксплуатировать | Windows-специфичность, устаревшая модель приложения, предел модернизации | Сильная зависимость от legacy, сейчас приоритет бизнеса — стабильная эксплуатация |
| Перейти на современный .NET, оставаясь на Windows | Можно модернизировать runtime и инструментарий. Большая выгода от производительности, опыта разработки, SDK-стиля | Зависимость от Windows API сохраняется. Кроссплатформенности не будет | Бизнес-приложения на WinForms / WPF, Windows-службах, использующие Windows API |
| Перейти на современный .NET с прицелом на Linux / контейнеры / облако в будущем | Растёт свобода в выборе места развёртывания. Легче обновить и модель эксплуатации | Windows-специфичные API и app model нужно снять заранее | Хотите перевести серверную часть в облако, обновить инфраструктуру |
Важно решить заранее не «хотим ли мы мигрировать», а куда мы хотим приземлиться после миграции.
3. Четыре решения, которые стоит принять заранее
3.1 Целевая версия .NET
На момент написания, согласно политике поддержки Microsoft, LTS-версией является .NET 10. При этом поддержка .NET 8 LTS и .NET 9 STS обе заканчиваются 2026-11-10.
Поэтому если вы только начинаете новую миграцию с .NET Framework, при отсутствии особых причин естественно выбрать в качестве цели текущую LTS.
Практический подход здесь прост.
- Для небольшой миграции, которую хочется завершить быстро, приземляйтесь сразу на текущую LTS
- Для основной системы с расчётом на долгую эксплуатацию тоже стоит по умолчанию ориентироваться на текущую LTS
- Причина вида «из-за существующей библиотеки хотим предыдущую LTS» вполне допустима, но решение стоит принимать, глядя на конкретную дату окончания поддержки
3.2 Остаться исключительно на Windows или в будущем нацелиться на кроссплатформенность
От этого решения сильно зависит, на что обращать внимание.
- Если остаётесь исключительно на Windows, можно взять реалистичный курс на модернизацию runtime с помощью WPF / WinForms и Windows Compatibility Pack.
- Если в будущем нацелены на Linux / контейнеризацию, нужно на раннем этапе провести инвентаризацию API, завязанных на Windows:
System.Drawing.Common, реестр, WMI, EventLog, службы Windows, COM, Office Interop.
Если начать миграцию, не приняв это решение, посреди пути разговор скручивается: «а стоило ли фиксироваться на Windows?», «нет, мы вообще-то хотели контейнеризацию».
3.3 Мигрировать одним махом или поэтапно
Существует, в целом, три формы миграции.
- одномоментная миграция, близкая к in-place
- миграция side-by-side, при которой старое и новое сосуществуют
- поэтапная миграция — постепенный перенос по маршрутам / библиотекам
В частности, для приложений на ASP.NET Framework руководство Microsoft однозначно рекомендует incremental migration. Если вы не хотите останавливать продакшен, у вас много функций и много сопутствующих зависимостей, разумнее с самого начала проектировать миграцию как поэтапную.
3.4 Что «исключить из текущего объёма миграции»
Миграции чаще всего проваливаются из-за того, что в них пытаются впихнуть слишком много.
Например, одновременное выполнение всего перечисленного обычно оказывается непосильным.
- .NET Framework → .NET
- ASP.NET Framework → ASP.NET Core
- EF6 → EF Core
- Windows-серверы → Linux-контейнеры
- смена платформы аутентификации
- смена платформы логирования / мониторинга
- миграция базы данных
Конечно, всё это когда-нибудь может понадобиться. Но вопрос нужно ли делать это одновременно — совсем другой.
На практике лучше работает такое разделение.
- Сначала модернизировать runtime и структуру проекта
- Затем перенести app model
- И в конце обновить ORM, аутентификацию, облако и мониторинг
4. Основа, которую стоит подготовить до начала работы
Руководство Microsoft по подготовке к портированию весьма практично. Если коротко: перед миграцией нужно привести текущий проект на .NET Framework ближе к «современному входу».
4.1 Переход на .NET Framework 4.7.2 или новее
Официальное руководство рекомендует перед портированием таргетировать .NET Framework 4.7.2 или новее. Причина в том, что даже когда .NET Standard не содержит существующий API как есть, становится проще перейти на более новую альтернативу API.
На практике, по возможности, понятнее ориентироваться на 4.8.1.
- проще с точки зрения поддержки
- удобно рассматривать как финальную стабильную точку на стороне .NET Framework
- легче выстроить политику «сначала навести порядок на стороне текущего Framework»
Что меняется, если сделать это заранее
- работа с общими библиотеками на
.NET Standard 2.0становится стабильнее - заранее снижается уровень шума, порождаемого устаревшим runtime
- проще отделить, вызвана ли проблема совместимости устаревшим Framework или переходом на современный .NET
4.2 Переход на PackageReference
Руководство по подготовке к портированию рекомендует перевести ссылки в формат PackageReference. Если сделать это заранее, управление зависимостями становится намного прозрачнее.
Что меняется при переходе на PackageReference
- ссылки на пакеты собираются в
csproj - транзитивные зависимости становятся легче обозримы
- допущения restore выравниваются со стороной современного .NET
- улучшается совместимость с CLI / CI
Однако здесь есть мины.
Типичные мины
Официальная документация NuGet явно указывает следующие ограничения при переходе с packages.config на PackageReference.
- встроенная миграция Visual Studio недоступна для проектов ASP.NET
- пакеты, зависящие от
install.ps1/uninstall.ps1, могут работать не так, как ожидается - активы в папке
contentиногда игнорируются - XDT-трансформации, такие как
web.config.install.xdt, не применяются - пакеты со старой структурой сборок прямо под
libмогут разрешаться некорректно
То есть не стоит считать это просто сменой формата пакетов. В классическом ASP.NET особенно распространена практика переписывания web.config при установке пакета NuGet, поэтому при миграции скрытые допущения легко всплывают на поверхность.
4.3 Переход на SDK-стиль
Руководство по подготовке к портированию также рекомендует перейти на формат проекта в SDK-стиле. Это даёт весьма ощутимый эффект.
Что меняется при переходе на SDK-стиль
csprojстановится значительно компактнее- хорошо сочетается с
PackageReference - упрощается multi-targeting
- проще выстроить CI/CD вокруг
dotnet build/dotnet test/dotnet publish - конфигурация приближается к стороне современного .NET, что сокращает объём различий на дальнейших этапах
Иначе говоря: если резко перейти на современный .NET, оставив старый csproj и старое управление NuGet, объём различий окажется слишком большим.
4.4 Обновляем зависимости заранее
Это тоже соответствует официальному руководству: зависимости стоит обновить до последних доступных версий, а по возможности — до версий, поддерживающих .NET Standard.
Зачем делать это заранее
- раньше становится понятно, «работает ли этот пакет на современном .NET»
- устаревшие зависимости не превращаются в шум
- проще перевести общие библиотеки на
netstandard2.0 - на дальнейшем этапе миграции проще сосредоточиться именно на «портировании кода»
4.5 Стоит проверить и допущения официальных инструментов
По состоянию на 2026-03 центр тяжести рекомендаций Microsoft сместился в сторону модернизации через GitHub Copilot. На практике удобнее рассматривать это не как отдельные традиционные инструменты миграции, а как единый поток поддержки, включающий оценку, планирование, исправление кода и проверку.
Однако текущая документация исходит из предпосылок: Visual Studio 2026 или поддерживаемая линейка Visual Studio 2022, GitHub Copilot и код на C#.
Зачем это проверять
- меняется то, чего можно ожидать от официальных инструментов
- можно выровнять допущения команды об IDE / build agent / расширениях
- можно принять взвешенное решение не слишком полагаться на автоматизацию в решениях на VB.NET
Проекты с примесью VB.NET — не редкость. Поэтому стоит заранее выяснить, насколько сильно способны помочь последние официальные инструменты.
5. Оцениваем сложность по типам проектов
О миграции часто говорят как об одной задаче «с .NET Framework на .NET», но на практике каждый тип проекта — это отдельная игра.
5.1 Приблизительное ощущение сложности
| Тип | Ощущение сложности | Основные вопросы |
|---|---|---|
| Библиотека классов | Низкая—средняя | Совместимость API, зависимости, разделение целевых платформ |
| Консоль / batch / часть служб Windows | Низкая—средняя | Способ распространения, нативные зависимости, конфигурация |
| WinForms / WPF | Средняя | Остаётся Windows-специфичным, дизайнер, сторонний UI, всё, что связано с BinaryFormatter |
| ASP.NET MVC / Web API | Средняя—высокая | Миграция app model на ASP.NET Core, аутентификация, сессии, конфигурация, DI |
| ASP.NET Web Forms | Высокая | Большая разница в модели страниц, предполагается замена слоя UI |
| Клиент WCF | Средняя | Замена пакетов, контракты, конфигурация |
| Сервер WCF | Высокая | CoreWCF или редизайн на gRPC / HTTP API |
| Одновременный переход EF6 → EF Core | Высокая | ORM — совершенно другая вещь, отличия в поведении, история миграций |
5.2 Для библиотек классов ключевое — как провести «границу совместного использования»
Библиотеки классов переносить относительно легко. Но только когда библиотека действительно отделена так, как и полагается библиотеке.
Сложность возрастает при наличии таких зависимостей:
- обращение к
System.Web - прямое использование
HttpContext.Current - типы WPF / WinForms в публичном API
- сильная опора на Windows API вроде реестра, WMI, EventLog
- зависимость от
AppDomainили Remoting
Проще всего рассуждать так: если удаётся выделить только бизнес-логику — задача лёгкая; если библиотека вобрала в себя ещё и app model — тяжёлая.
5.3 WinForms / WPF мигрируют легко, но остаются Windows-специфичными
WinForms и WPF можно перевести на .NET. Однако оба остаются исключительно Windows-фреймворками.
Ошибиться в ожиданиях здесь опасно.
- Что улучшится
- можно перейти на runtime, язык и SDK-стиль современного .NET
- легче осовременить CI/CD и управление пакетами
- легче получить отдельные улучшения производительности и сопровождаемости
- Что не изменится
- приложение останется Windows-специфичным
- сохранятся проблемы совместимости UI-контролов и компонентов дизайнера
- никуда не денутся проблемы ActiveX / COM / нативных DLL
Кроме того, в WinForms / WPF нередко требуется проверить влияние BinaryFormatter. Особенно если custom-типы задействованы в clipboard, drag & drop, ResX или сериализации на этапе дизайна, проблема легко всплывает при переходе на .NET 9 или новее.
5.4 ASP.NET Framework — это «миграция app model», а не «миграция runtime»
Миграция с ASP.NET Framework на ASP.NET Core прямо названа в руководстве Microsoft non-trivial. Причина не просто в изменении имён API, а в том, что отличается сама лежащая в основе архитектура.
Различия чаще всего проявляются в следующем:
- модели хостинга (Hosting model)
- конвейере middleware
- модели обработки запросов
- Session / Cache
- аутентификации / авторизации
- конфигурации
- Dependency Injection
- логировании / мониторинге
Для приложения на ASP.NET Framework в первую очередь стоит проверить следующее.
- какие route / endpoint можно перенести первыми
- можно ли снять зависимость от
System.Webс общих библиотек - как выровнять аутентификацию / сессии / обработку исключений / логирование
- будет ли миграция поэтапной без остановки продакшена
Особенно для крупных приложений реалистичнее с самого начала закладываться на incremental migration.
5.5 С Web Forms нужно начинать не с «переноса активов», а с «разбиения ответственности»
Web Forms — это не та же app model, что у ASP.NET Core. Поэтому при оценке безопаснее не исходить из того, что экранные активы можно перенести как есть.
На практике чаще всего начинают со следующего разбиения.
- отделить логику экрана от бизнес-логики
- разложить ответственности, зашитые в
Page/UserControl/ViewState - вынести бизнес-логику и доступ к данным в общую библиотеку
- перестроить UI на другой модели: Razor Pages / MVC / Blazor и т. п.
То есть для проекта на Web Forms важно не столько наличие плана миграции runtime, сколько наличие плана разбиения ответственности.
5.6 Клиента WCF и сервер WCF нужно рассматривать отдельно
Их не стоит смешивать в одну кучу.
Клиент WCF
Для WCF Client существуют поддерживаемые NuGet-пакеты для современного .NET. Поэтому если вы только вызываете WCF, задача иногда оказывается не такой тяжёлой, как кажется на первый взгляд.
Сервер WCF
С другой стороны, сторона, хостящая службу WCF, — совсем другое дело. В руководстве Microsoft описаны в основном два пути модернизации.
- использовать CoreWCF, сохраняя совместимость с существующими клиентами
- перейти на современные RPC / HTTP-стеки вроде gRPC
Однако CoreWCF — это не полный перенос WCF, а подмножество (subset). Он подходит для сохранения совместимости с существующими клиентами, но изменения кода и тестирование обязательны.
6. Выявляем технологии, недоступные в .NET или создающие проблемы «как есть»
Это абсолютно необходимо сделать до начала работы. У Microsoft есть перечень технологий, доступных в .NET Framework, но недоступных в .NET 6+.
6.1 Технологии, которые часто становятся красным флагом
| Технология | Статус в .NET | Как к ней относиться |
|---|---|---|
Создание AppDomain, например AppDomain.CreateDomain |
Не поддерживается | Изоляцию рассматривать через отдельный процесс / контейнер / AssemblyLoadContext |
| .NET Remoting | Не поддерживается | Переход на IPC, HTTP, gRPC, Socket, Pipe и т. п. |
| CAS / Security Transparency | Не поддерживается как граница безопасности | Рассматривать через OS / контейнер / разделение привилегий |
System.EnterpriseServices (COM+) |
Не поддерживается | Изолировать и заменять дизайн, завязанный на COM+ |
| Workflow Foundation | Не поддерживается | Рассматривать отдельной оценкой, включая альтернативы вроде CoreWF |
| Сервер WCF | В коробочном виде не работает как есть | Выбирать между CoreWCF и gRPC |
| BinaryFormatter | Начиная с .NET 9 реализация всегда выбрасывает исключение | Миграция сериализатора, аудит ResX / clipboard / drag & drop |
6.2 AppDomain: «часть API осталась, но создание — отдельный вопрос»
Тема AppDomain немного запутанная. В .NET часть поверхности API сохранена, но использование в духе создания нового AppDomain для изоляции не поддерживается.
Поэтому если AppDomain использовался для следующих целей, потребуется редизайн.
- изоляция плагинов
- выгрузка динамически загруженного кода
- изоляция кода с частичным доверием
- временная изоляция среды выполнения
Перед миграцией важно смотреть не только на то, встречается ли слово AppDomain, но и на то, для чего именно он использовался.
6.3 Remoting «глубже, чем кажется»
Помимо самого Remoting, в зону влияния может попасть и такое, как вызовы BeginInvoke() / EndInvoke() для асинхронных делегатов. Это не сам Remoting, но он не поддерживается в современном .NET, поэтому это тоже нужно выявить перед миграцией.
Поэтому при поиске безопаснее проверить сразу и это:
System.Runtime.RemotingMarshalByRefObjectRealProxyBeginInvoke(/EndInvoke(
6.4 BinaryFormatter внезапно выходит на первый план в зависимости от target version
Чем старше кодовая база, тем выше шанс, что BinaryFormatter используется «неосознанно».
- сохранённые данные
- кэш
- хранение сессий
- состояние плагинов
- clipboard / drag & drop
- ResX
- всё, что связано с дизайнером WinForms / WPF
Начиная с .NET 9 реализация BinaryFormatter отсутствует в runtime, и API всегда выбрасывает PlatformNotSupportedException. То есть это не «подумаем позже», а вопрос, который нужно проаудировать сразу, как только определена target version.
6.5 Поисковые термины, которые стоит проверить через grep в первую очередь
Ещё до начала работы достаточно поискать эти термины по всему решению — картина заметно проясняется.
System.Web
HttpContext.Current
System.Runtime.Remoting
MarshalByRefObject
AppDomain
BinaryFormatter
ServiceHost
ChannelFactory
System.EnterpriseServices
Workflow
packages.config
web.config.install.xdt
install.ps1
DllImport
AxInterop
Microsoft.Office.Interop
Найти хотя бы один из этих терминов не значит «сразу мимо». Это карта, которая показывает, что можно мигрировать по стандартному маршруту, а что окажется на отдельном треке.
7. Решаем, насколько допустима зависимость от Windows
Частое заблуждение при миграции — «если перейти на .NET, приложение станет кроссплатформенным». Такой магии не существует. Если приложение глубоко связано с Windows, после миграции оно, естественно, останется Windows-специфичным.
7.1 Мигрировать, оставаясь исключительно на Windows, — вполне реалистичный путь
У Microsoft есть Windows Compatibility Pack — средство, позволяющее использовать из современного .NET многие Windows-специфичные API: реестр, WMI, EventLog, службы Windows, Directory Services и другие.
На практике его существование очень важно. Для команд, у которых
- в приоритете сначала перейти на современный .NET,
- но пока не планируется выходить за пределы Windows,
- и потому зависимость от Windows API готовы временно допустить,
это становится сильным вариантом.
Первой целью миграции вовсе не обязательно должна быть кроссплатформенность.
7.2 Но Windows-специфичные API — это ещё и «долг, который аукнется позже»
Наличие Windows Compatibility Pack не означает, что можно расслабиться во всём. Если у вас есть цели вроде:
- разместить приложение в Linux-контейнере,
- запускать его с прицелом на Kubernetes,
- чтобы разработчики на macOS / Linux собирали тот же build,
- в будущем сократить число Windows VM в облаке,
лучше уже сейчас сделать видимой зависимость от Windows API.
7.3 System.Drawing.Common особенно часто понимают неправильно
System.Drawing.Common, начиная с .NET 6, — это исключительно Windows-специфичная библиотека. Если у вас есть код, использующий её для обработки изображений или отрисовки текста, сначала нужно решить, каким путём идти.
- продолжать эксплуатацию на Windows
- или в будущем хотите запускать также на Linux / macOS
В первом случае иногда можно пока оставить всё как есть. Во втором — с самого начала включить в план миграции замену на, например, SkiaSharp или ImageSharp.
7.4 Типичные признаки жёсткой привязки к Windows
Если присутствуют такие ссылки или API, безопаснее оценивать проект исходя из того, что «как минимум сначала он мигрирует, оставаясь исключительно Windows-специфичным».
Microsoft.Win32.RegistrySystem.ManagementSystem.Diagnostics.EventLogSystem.ServiceProcessSystem.DirectoryServicesSystem.DrawingDllImport/ P/Invoke- ссылки на COM
AxInterop.*Microsoft.Office.Interop.*
8. Способ выделения общих библиотек влияет на сложность
Для крупных решений не будет преувеличением сказать, что успех миграции определяется тем, как разрезаны общие библиотеки.
8.1 Сначала классифицируем
Библиотеки проще упорядочить, разбив на три большие категории.
- чистая бизнес-логика / доменная логика
- промежуточный слой с небольшой зависимостью от app model
- слой, тесно связанный с UI / Web / Windows API
Из них в первую очередь стоит переносить категорию 1.
- вычисления
- проверка правил
- DTO / контракты
- доменные сервисы
- простые преобразования данных
Если чисто выделить эту часть, сложность резко снижается.
8.2 netstandard2.0 по-прежнему остаётся рабочим мостом
Согласно рекомендациям Microsoft, для общей библиотеки, которая должна сосуществовать также и со стороной .NET Framework, базовым вариантом является .NET Standard 2.0.
Здесь важны два момента.
- .NET Framework не поддерживает
.NET Standard 2.1 - если общую библиотеку нужно использовать и из старого, и из нового кода, 2.0 обычно оказывается практическим решением
8.3 Что меняется при переходе на netstandard2.0
| Подход | Что меняется | Подходящий случай | Что учесть |
|---|---|---|---|
Переход на netstandard2.0 |
Легко ссылаться и из старого, и из нового кода | Чистая бизнес-логика, общие контракты, утилиты | API, специфичные для app model, разместить нельзя |
Multi-target (например net48;net10.0) |
Общий код сохраняется, при этом можно хранить различия по окружениям | Библиотеки с небольшими различиями по окружениям | Растёт объём условной компиляции и управления сборкой |
Сразу перейти только на net10.0 |
В перспективе самый чистый вариант | Новые слои, которым не нужно сосуществование старого и нового | Нельзя ссылаться из .NET Framework |
8.4 Режим совместимости — не панацея
У .NET Standard 2.0 есть режим совместимости, позволяющий ссылаться на библиотеки .NET Framework. Однако это не волшебство, при котором всё прозрачно работает само по себе.
Например, библиотеки, полагающиеся на специфичные для app model API вроде WPF, остаются, естественно, сложными. То есть даже говоря «общая библиотека», важно сузить её до тех ответственностей, которые действительно можно разделять.
8.5 Для библиотек, связанных с ASP.NET, всё решает возможность снять зависимость от System.Web
При поэтапной миграции ASP.NET Framework сильно осложняет жизнь ситуация, когда общая библиотека напрямую зависит от HttpContext.Current или System.Web.
Базовая стратегия в этом случае — один из следующих вариантов.
- вынести зависимость от
System.Webза пределы интерфейса - изменить код так, чтобы информация, происходящая от
HttpContext, принималась в виде DTO - на переходный период использовать адаптеры
- если и это не помогает — снимать зависимость поэтапно с помощью multi-target
8.6 Библиотеки обновляем в порядке leaf-first
В руководстве по incremental migration для ASP.NET явно указано обновлять вспомогательные библиотеки в порядке postorder depth-first, то есть начиная с листьев.
Это работает не только для Web, но и для обычных решений в целом.
- поскольку зависимости обновлены первыми, верхние слои становятся более обозримыми
- проблемы совместимости легче локализовать
- легче тестировать каждую библиотеку по отдельности
9. Инвентаризируем NuGet / внешние зависимости / сторонние компоненты
Если сделать это небрежно, самая большая боль настигнет во второй половине миграции.
9.1 Зависимости проще упорядочить, разбив на 4 категории
- публичные пакеты NuGet
- внутренние private-пакеты / internal-библиотеки
- локальные ссылки на DLL
- COM / ActiveX / нативные DLL / SDK
Смотреть только на категорию 1 недостаточно. По-настоящему опасны 3 и 4.
9.2 Что проверять по каждой зависимости
По каждой зависимости стоит проверить как минимум следующее.
- таргетирует ли она современный .NET
- поддерживает ли
PackageReference - нормально ли работает с SDK-стилем
- нет ли ограничений по x86 / x64 / ARM64
- не зависит ли от инструментов дизайн-тайма или расширений Visual Studio
- не предполагает ли install script / config transform
- продолжается ли поддержка
9.3 Сторонний UI / отчёты / компоненты дизайн-тайма оценивайте отдельно
При миграции WinForms / WPF / ASP.NET это ощущается особенно сильно.
- сетки (grid)
- движки отчётов
- компоненты вывода PDF
- компоненты графиков
- UI-библиотеки, интегрированные с дизайнером
- обёртки ActiveX
Здесь задействован не только runtime, но и поддержка в дизайн-тайме. Если при оценке миграции смотреть только на «компилируется ли», можно легко промахнуться.
9.4 Обязательно проверяйте нативные DLL и разрядность
Даже если в эпоху .NET Framework приложение выглядело работающим как AnyCPU, на самом деле оно может зависеть от такого:
- COM, жёстко привязанный к
x86 - 32-битный ActiveX
- определённая версия VC++ Runtime
- подписанные нативные DLL
Это не проблемы, внезапно появившиеся из-за перехода на современный .NET, — это лишь всплывающие на поверхность изначально существовавшие ограничения. Именно поэтому их стоит выявить заранее, до миграции.
10. Рассматриваем EF6, сериализаторы и данные как отдельную проблему
Миграцию runtime и редизайн доступа к данным / сериализации лучше по возможности рассматривать как раздельные задачи — так дело идёт успешнее.
10.1 EF6 → EF Core — это не прямое обновление
В руководстве Microsoft по EF тоже указано, что EF Core — это полная переработка (total rewrite) EF6, и прямого пути обновления не существует.
Поэтому для приложений с EF6 реалистична такая последовательность.
- Сначала перейти на современный .NET
- При необходимости продолжать работу с EF6, оставив его без изменений
- Затем перевести на EF Core уже отдельным проектом
Не совмещайте миграцию runtime и миграцию ORM. Уже одно это заметно снижает сложность.
10.2 Что меняется, если оставить EF6
- Плюсы
- различия в слое доступа к данным можно отложить
- можно сосредоточиться на миграции business logic и app model
- «отличия в поведении EF Core» не примешиваются
- На что обратить внимание
- с точки зрения новой разработки основной вариант — EF Core
- у формата использования EF6 Designer / EDMX есть отдельные ограничения
10.3 Для EF6 на базе EDMX учитывайте и «дизайн-тайм»
В документации EF6 указано, что EF Designer напрямую не поддерживается в проектах .NET / .NET Standard, а также в проектах .NET Framework в SDK-стиле.
Для приложений на базе EDMX эти три момента нужно рассматривать по отдельности.
- работает ли это во время выполнения
- можно ли использовать Designer
- как поступить со сгенерированным кодом
Если EDMX используется активно, безопаснее сразу включить это в первоначальную оценку.
10.4 BinaryFormatter и собственная сериализация легко становятся «скрытой зависимостью»
Сериализаторы легко упустить, если полагаться только на поиск по коду.
- формат сохранения данных
- обмен сообщениями
- кэш
- старые контракты WCF / SOAP
- ResX
- clipboard / drag & drop
Здесь замешана ещё и совместимость данных. То есть нужно проверить не просто «собирается ли», а можно ли прочитать старые данные.
11. Включаем в объём миграции конфигурацию, распространение, эксплуатацию и CI/CD
Объект миграции — не только исходный код.
11.1 Файлы конфигурации
На стороне .NET Framework в app.config / web.config нередко хранится очень многое.
- строки подключения
- пользовательские секции конфигурации
- настройки endpoint WCF
- binding redirect
- diagnostics
- различные настройки ASP.NET
- результаты трансформаций, применённых при установке пакетов
На стороне современного .NET способ хранения и путь загрузки конфигурации в некоторых случаях меняется. Поэтому «файлы конфигурации оставим на потом» — опасный подход.
Первый шаг — инвентаризация конфигурации.
- что находится в файлах конфигурации
- что из этого обязательно при запуске приложения
- что относится к различиям по окружениям
- что было автоматически внедрено через NuGet или установщик
11.2 Способ распространения
Форму распространения тоже стоит проверить до начала работы.
- работает ли под IIS
- служба Windows ли это
- Scheduled Task ли это
- ClickOnce / MSI / собственный установщик
- предполагается ли on-premises сервер
- что подходит лучше: self-contained или framework-dependent
Даже если сам исполняемый модуль перенесён, если механизм распространения и запуска остаётся привязан к старым допущениям, в конце всё упирается в тупик.
11.3 Логирование, мониторинг, регламенты эксплуатации
Эксплуатационную сторону тоже легко упустить.
- предполагается ли Windows Event Log
- используются ли Performance Counter
- есть ли мониторинг на базе WMI
- зафиксированы ли служебная учётная запись и права
- предполагается ли, что логи пишутся в локальный файл
Совершенно обычная ситуация: код после миграции работает, а эксплуатация не выстраивается.
11.4 CI/CD и build agent
Перед миграцией стоит проверить также следующее.
- можно ли установить нужный .NET SDK на build agent
- что делать с pipeline, завязанным на
nuget.exe/msbuild.exe - переходить ли на основу
dotnetCLI - как обновить задания тестирования, coverage, publish
- не завязаны ли внутренние шаблоны или reusable pipeline на старый формат
«У меня локально всё работает, а CI падает» — классика миграции.
12. Реалистичный порядок выполнения миграции
С учётом всего вышесказанного реалистичный порядок работы обычно сводится примерно к следующему.
12.1 Сначала наводим порядок на стороне текущего Framework
- Перейти на .NET Framework 4.7.2 или новее, желательно на 4.8.1
- Обновить зависимости
- Пересмотреть
packages.config - По возможности перейти на
PackageReferenceи SDK-стиль - Убедиться, что текущее приложение в этом состоянии действительно работает
Уже одно это заметно сокращает объём различий на следующем этапе.
12.2 Сначала спасаем общие библиотеки
Далее переводим бизнес-логику и общие контракты на netstandard2.0 или multi-target. Базовый порядок обновления — leaf-first.
12.3 Для самого приложения меняем стратегию в зависимости от app model
- Библиотеки классов / консоль / часть служб переносятся сравнительно прямолинейно
- WinForms / WPF модернизируются, оставаясь Windows-специфичными
- ASP.NET MVC / Web API небольшие — одним махом, тяжёлые — поэтапно
- Web Forms исходить из замены экранных активов, сначала вынести общую логику
- Сервер WCF сначала решить: сохранять CoreWCF или делать редизайн на gRPC
12.4 Соблюдаем правило «не делать всё сразу»
Особенно стоит избегать таких сочетаний.
- миграция runtime + полная замена ORM
- миграция runtime + смена платформы аутентификации
- миграция runtime + полный переход в облако
- миграция runtime + смена платформы мониторинга
- миграция runtime + обновление UI-фреймворка
Даже если всё это в итоге нужно, обычно лучше работает подход не накапливать всё в одном спринте.
12.5 Действуем только после того, как зафиксированы тесты и базовые показатели
Как минимум перед началом работы желательно подготовить следующее.
- модульные тесты
- интеграционные тесты для основных бизнес-процессов
- снимок (snapshot) состояния для типичных экранов / API
- базовые показатели производительности
- способ проверки основных логов
- процедуру отката
Двигаться вперёд, не имея возможности после миграции определить, «что именно сломалось», — весьма опасно.
13. Чек-лист перед началом работы
Ниже приведён чек-лист, который можно вставить прямо в систему управления проектом.
13.1 Общая политика
- Можете одним предложением объяснить, зачем нужна миграция
- Определено, куда мы приземляемся: Windows-специфичный современный .NET или будущая кроссплатформенность
- Определена целевая версия .NET
- Определено, что исключено из текущего объёма (переход на EF Core, обновление аутентификации, полная миграция в облако и т. п.)
13.2 Подготовка текущей стороны .NET Framework
- Перешли на .NET Framework 4.7.2 или новее, желательно на 4.8.1
- Обновили зависимости до актуальных версий
- Выявили наличие
packages.config - Проверили возможность перехода на
PackageReference - Проверили возможность перехода на SDK-стиль
- Текущее приложение в этом состоянии собирается, запускается и проходит тесты
13.3 Тип приложения и выбор технологий
- Разбили сложность по типам: библиотека классов / десктоп / Web / WCF и т. д.
- Понимаем, что WinForms / WPF остаются Windows-специфичными
- Понимаем, что ASP.NET Framework — это миграция app model на ASP.NET Core
- Включили в оценку замену слоя UI для Web Forms
- Оценили клиента и сервер WCF по отдельности
13.4 Неподдерживаемые технологии и API, требующие внимания
- Выявили зависимости от
AppDomain - Выявили Remoting /
MarshalByRefObject/BeginInvoke/EndInvoke - Выявили CAS / Security Transparency / COM+ / WF
- Выявили зависимости от BinaryFormatter
- Выявили зависимости от
System.Web
13.5 Зависимости, специфичные для Windows
- Выявили использование реестра, WMI, EventLog, служб Windows, Directory Services
- Выявили использование
System.Drawing.Common - Выявили COM / ActiveX / Office Interop / P/Invoke / нативные DLL
- Проверили ограничения x86 / x64 / ARM64
13.6 Общие библиотеки и доступ к данным
- Классифицировали общие библиотеки на бизнес-логику / слои, тесно связанные с app model
- Выявили то, что можно перевести на
netstandard2.0 - Выявили библиотеки, которым нужен multi-target
- Определили, можно ли сначала перенести только runtime, сохранив EF6
- Проверили зависимость от EDMX / Designer
13.7 Эксплуатация и сборка
- Провели инвентаризацию файлов конфигурации
- Проверили способ распространения (IIS / Service / MSI / ClickOnce и т. д.)
- Проверили допущения о логировании / мониторинге / правах / служебной учётной записи
- Проверили, нужно ли обновление CI/CD и build agent
- Подготовили процедуру отката
14. Итог
В миграции с .NET Framework на .NET важнее не «какой командой мигрировать», а заранее разглядеть, что переносится как есть, а что становится отдельной проблемой.
Если сузить до главного, это шесть пунктов.
- навести порядок на стороне .NET Framework перед миграцией
- разделить сложность по app model
- заранее выявить неподдерживаемые технологии
- решить, насколько допустима зависимость от Windows
- решить, как резать общие библиотеки
- не перегружать один этап миграцией ORM, аутентификации и облака одновременно
Точность оценки миграции сильно зависит от первой недели работы. Иначе говоря, если за эту неделю удастся упорядочить все вопросы, вторая половина будет заметно ближе к обычной разработке.
«Для начала просто попробуем переключиться на net10.0» — неплохой вариант в качестве разведки. Но для боевой миграции перед этим есть на что посмотреть. Именно это мы и разобрали в этой статье.
15. Справочные материалы
- Предварительные условия для портирования кода
- Обзор портирования с .NET Framework на .NET
- Технологии .NET Framework, недоступные в .NET 6 и более поздних версиях
- Что такое модернизация с GitHub Copilot
- Установка модернизации с GitHub Copilot
- Официальная политика поддержки .NET
- Официальная политика поддержки .NET Framework
- Миграция ASP.NET Framework на ASP.NET Core с помощью инструментов
- Миграция с ASP.NET Framework на ASP.NET Core
- Get started with incremental ASP.NET to ASP.NET Core migration
- Use the Windows Compatibility Pack to port code to .NET
- .NET Standard
- Cross-platform targeting for .NET libraries
- Миграция с packages.config на PackageReference
- PackageReference in project files
- BinaryFormatter migration guide
- Руководство по миграции BinaryFormatter для Windows Forms
- WCF Client Support Policy
- CoreWCF Support Policy
- Why migrate WCF to ASP.NET Core gRPC
- Port from EF6 to EF Core
- Новые возможности EF6
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Версионирование схемы БД бизнес-приложения — практика миграций, предотвращающая «у каждого клиента своя база»
Практическое руководство по версионированию схемы БД бизнес-приложения, установленного у множества клиентов. Разбираем PRAGMA user_versio...
До каких пор будут работать приложения VB6 ── состояние поддержки среды выполнения и практичный путь миграции на .NET
До каких пор будут работать приложения VB6? В статье разбирается асимметрия между политикой поддержки среды выполнения VB6 (поддерживаетс...
CI/CD для приложений WinForms / WPF на практике — автоматизация от сборки до подписи и распространения через GitHub Actions
Практическое руководство по настройке CI/CD для приложений WinForms / WPF через GitHub Actions. Минимальный YAML для сборки и тестов на w...
Если ваше Windows-приложение приняли за вирус — как реагировать на ложные срабатывания Microsoft Defender и жить с влиянием на производительность
Разбираем правильный порядок действий, если Microsoft Defender ложно определяет ваше Windows-приложение как вредоносное: как устроена сов...
Спящий режим, гибернация, Modern Standby и долго работающие приложения — как проектированием предотвратить «остановилось ночью»
Разбираем, почему долго работающее Windows-приложение оказывается «остановленным к утру», начиная с различий между спящим режимом S3, гиб...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Использование и перенос существующих активов
Это упорядочивание существующих активов, связанных с .NET Framework, Web Forms, WCF, COM / ActiveX и устаревшей практикой работы с NuGet, поэтому тема хорошо подходит для консультаций по миграции унаследованных активов.
Технические консультации и ревью дизайна
Если нужно заранее продумать масштаб миграции, разбиение на этапы и то, насколько допустима зависимость от Windows, эта тема хорошо подходит для технической консультации и ревью архитектуры.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Что нужно сделать в первую очередь перед миграцией с .NET Framework на .NET?
- Прежде чем приступать к реализации, нужно навести порядок на стороне .NET Framework. Официальное руководство Microsoft тоже рекомендует перед портированием перейти на .NET Framework 4.7.2 или новее (на практике, по возможности, — на 4.8.1), перевести packages.config на PackageReference, перейти на формат проектов в SDK-стиле и обновить зависимости до актуальных версий. Если сделать это заранее, объём различий на этапе перехода на современный .NET заметно сократится, и станет проще отделить, вызвана ли проблема совместимости устаревшим Framework или самим переходом на .NET. Вместе с этим до начала работы стоит провести инвентаризацию неподдерживаемых технологий — AppDomain, Remoting, BinaryFormatter и подобных.
- Может ли решение остаться на .NET Framework быть правильным?
- Да, это совершенно нормальное решение. .NET Framework 4.8.1 продолжает поддерживаться до тех пор, пока работает на поддерживаемой версии Windows, поэтому речь не идёт о том, что если не перевести всё на современный .NET прямо сейчас, компания сразу окажется в опасности. Если у вас огромный массив экранов на Web Forms, требуется строго сохранить совместимость сервера WCF, есть глубокая зависимость от Workflow Foundation или COM+, либо сторонние компоненты для дизайнера не поддерживают современный .NET, разумно пока стабильно эксплуатировать приложение на 4.8.1, параллельно выстраивая план замены на отдельном треке. Однако при этом сохраняются ограничения: невозможность выйти за пределы Windows и трудность получить выгоду от новой производительности и языковых возможностей .NET.
- Какие технологии не переносятся на .NET или легко создают проблемы?
- Создание AppDomain, .NET Remoting, CAS (Code Access Security), COM+ (System.EnterpriseServices) и Workflow Foundation не поддерживаются в современном .NET — это красные флаги, требующие пересмотра дизайна. Сервер WCF из коробки напрямую не работает — придётся выбирать между CoreWCF и редизайном на базе gRPC / HTTP API. Начиная с .NET 9 реализация BinaryFormatter отсутствует и API всегда выбрасывает исключение, поэтому нужен аудит, охватывающий сохранённые данные, ResX, clipboard и drag & drop. Помимо этого, стоит внимательно отнестись к install.ps1 / XDT-трансформациям packages.config, нативным DLL, COM / ActiveX и предположениям о x86 — эти места легко проходят сборку, но падают именно во время выполнения.
- Если перевести приложение на WinForms или WPF на .NET, станет ли оно кроссплатформенным?
- Нет, не станет. WinForms / WPF можно перевести на .NET, но они остаются Windows-специфичными фреймворками. Миграция даёт такие преимущества, как современный runtime .NET, языковые возможности, SDK-стиль проектов и совместимость с CI/CD, но приложение не станет запускаться в Linux-контейнере. Если в будущем планируется Linux / контейнеризация, стоит заранее провести инвентаризацию API, завязанных на Windows: System.Drawing.Common, реестр, WMI, EventLog, Windows-службы, COM, Office Interop. Если же вы остаётесь исключительно на Windows, реалистичный путь — сначала модернизировать runtime с помощью Windows Compatibility Pack.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.
Публичные ссылки