Описание на проекта

Преди година работих по дългосрочен ASP.NET MVC проект, разработван с целите да се продава като продукт. Проектът представляваше система за управление на версиите или „Version control” клиент, който трябваше да позволява интеграция с TFS, SVN и Git, различавайки се от типичните version control клиенти с това, че позволяваше доста допълнителни възможности за анализ на кода. В началото version control клиентът работеше единствено под Windows среда.

С навлизането на ASP.NET Core на пазара, в един момент от разработката на проекта бе решено, проектът да се мигрира към ASP.NET Core за да може продуктът да се използва под Mac и Linux.

Участвайки активно в проекта, в етапа на миграция се запознах с някои добри практики при миграция на ASP.NET MVC проекти към ASP.NET Core MVC, които ще споделя в тази статия.

Анализ на миграция към ASP.NET Core MVC

Преди да започнем миграцията на проекта, тъй като бяхме напреднали с него трябваше да анализираме, каква част от проекта може да се мигрира директно и какво ще трябва да променим за да бъде успешна миграцията.

Анализът бе полезен с това, че ни помогна да реализираме миграцията по-плавно, както и ни помогна да естимираме сравнително точно, необходимото време за извършване на мигацията.

Инструментите, които използвахме за анализа и планирането на миграцията на проекта бяха Portability анализаторите.

Един много удобен portability анализатор е https://icanhasdot.net/.

Единственото което се иска от този онлайн анализатор е да качим нашите packages.config и *.csproj файлове и може да получим детайлна картина за нашите библиотеки – кои от тях могат да се презизползват, кои от тях имат .NET Core заместител и кои не поддържат .NET Standard спецификацията.

Сайтът проверява базата данни на nuget.org за библиотеките в проекта, затова ако проектът съдържа разработени от нас nuget пакети, сайтът няма да може да ги намери и анализира и ще ги маркира като “Not fount” – не открити в nuger.org пакети.

Допълнително nuget пакетите, които не съдържат .NET асемблита също са описани в анализа.

На картинката по-горе съм показал визуален анализ на пакетите използвани в един от последните ми проекти. Към всяка от посочените библиотеки има кратко описание и линкове към страници с допълнително детайли, библиотеки заместители в .NET Core и др. Поради дължината на този анализ, не съм го споделил в тази статия.

Можете да разгледате демото на същия сайт за да се запознаете с подобен кратък анализ.

Друг особенно полезен portability анализатор е .NET portability анализаторът, който може да се инсталира като допълнителен инструмент на Visual Studio от тук.

След инсталация инструментът може да се стартира от меню Tools, подменю Options, като се отиде на страница .NET Portability Analyzer. На подстраницата General, може да се избере платформата към която искаме да мигрираме, както може да се види на снимката по-долу. В случая на проекта, това беше платформата .NET Core 2.0.

Както се вижда от снимката, инструментът е много удобен тъй като може да анализира възможностите за миграция към голям брой платформи. След извършване на анализа се генерира подробен репорт, който може да се отвори в Excel, съдържащ интересна информация сред която дори информация какъв процент от кода на приложението подлежи на миграция.

Повече за използването на инструмента може да се прочете тук.

Ако не искаме да инсталираме този инструмент във Visual Studio, той може да се свали и от тук, като конзолно приложение.

Детайна информация за командите, които могат да се изпълнят с това конзолно приложение може да се намери тук.

Миграция към ASP.NET Core MVC – първи стъпки

Първоначално трябва да създадем нов ASP.NET Core проект. Можем да използваме „Empty” темплейт за да създадем празен проект или някой от другите темплейти примерно „Web Application (Model-View-Controller)” за да можем да използваме генерираните от този темплейт код и архитектура.

Този темплейт е особенно удобен ако решим да създадем чисто нов проект и в даден момент имаме проблем, тъй като може да сравним нашия код с този генериран от готовия темплейт и да идентифицираме причината за проблемите.

Трябва да изберем и вида автентикация, който ще използваме.

Както може да се види от снимката по-горе, вариантите ни са следните:

  • No Authentication – избирайки тази опция се задължаваме сами да изградим и управляваме потребителската си база.
  • Individual User Accounts – избирайки тази опция, към проекта ни се добавя потребителската система ASP.NET Core Identity, която ще разгледаме в повече детайли по-долу в тази статия.
  • Work or School Accounts – избирайки тази опция, имаме възможност да използваме услугата Azure Active Directory като метод за автентикация.
  • Windows Authentication – подходяща опция ако проектът ни представлява сайт част от Intranet-a на клиента, т.е. вътрешен за клиента ни сайт.

Избор на Individual User Accounts автентикация

За разработвания проект избрахме Individual User Accounts автентикацията, тъй като разработвахме „Version control” клиент, който трябваше да може да се инсталира на клиентска машина. Т.е. не можехме да използваме Windows Authentication опцията, която би ни ограничила до използване на конкретна активна директория или Work or School Accounts опцията, тъй като нямахме работа с Azure.

Затова избрахме Individual User Accounts автентикацията – за да използваме добрите практики положени от потребителската система ASP.NET Core Identity в изграждането на нашата автентикация.

Избирането на опцията Individual User Accounts автоматично извърши следните операции, които ние би трябвало да извършим ако искаме да добавим ASP.NET Core Identity в проект създаден без автентикация:

Инсталиране на пакетите Microsoft.AspNetCore.Identity.EntityFrameworkCore и Microsoft.AspNetCore.Authentication.Cookies

Конфигуриране на Identity услугата в метод ConfigureServices във файл Startup.cs.

Дефиниране на класовете ApplicationUser и ApplicationDbContext, които се използват от Identity услугата.

Детайлно описание на ASP.NET Core Identity автентикацията и други видове автентикации в ASP.NET Core може да прочетем в документацията на Microsoft за .NET Core тук – https://docs.microsoft.com/en-us/aspnet/core/security/?view=aspnetcore-2.1

Конфигуриране на ASP.NET Core MVC

Тъй като избрахме използването на темплейт „Web Application (Model-View-Controller)”, конфигурирането нa MVC бе извършено от темплейта вместо нас, но ако искаме да започнем с празен темплейт трябва да променим Startup.cs класа по следния начин:

Въпреки, че използвахме готов MVC темплейт в нашия проект, това което трябваше да направим сами бе мигрирането на функционалност от стартия ASP.NET MVC проект към ASP.NET Core MVC проекта.

Първоначално мигрирахме MVC часта – моделите, изгледите и контролерите, като под „мигриране“ се има в предвид копиране на класове от папки Models, Views и Controllers в съответните папки в новия проект.

Разбира се, взехме в предвид резултатът от portability анализаторите, заменяйки определени библиотеки с библиотеки на .NET Core. В случая с библиотеките, които не можахме да заменим се наложи в цялостна промяна на имплементацията на функционалностите, в които те се използваха.

Всякакъв вид статични файлове – изображения, стилове и скриптове копирахме в wwwroot папката на ASP.NET Core MVC проекта, в съответните подпапки css, images, scripts и т.н.

За да използваме в последствие тези файлове в нашето приложение трябва да добавим следния ред в метод Configure на файл Startup.cs.

app.UseStaticFiles();

Детайлна информация за добавяне на статични файлове в ASP.NET Core MVC проект може да прочетем в документацията на Microsoft тук.

За конфигуриране на bundling и minification в проекта, ASP.NET Core разполага с bundleconfig.json файл. Тъй като използвахме готов MVC темплейт, тази конфигурация бе създадена от темплейта за нас. В случай че не използваме готов MVC темплейт трябва да добавим следния код в bundleconfig.json.

Тъй като при мигриране на код между проектите неминуемо ще допуснем грешки, които дори ако не се появят при компилиране на проекта биха се появили при визуализация на дадена страница, разполагаме с опция да използваме специална страница за визуализиране на грешките при разработка. Конфигурирането на тази страница също бе добавено от готовия MVC темплейт, но е добре да знаем как се прави тази конфигурация в случай, че започнем проекта на чисто.

Страницата се конфигурира със следния код в метод Configure в клас Startup.cs.

Както може да се види от кода, в случай че използваме средата за разработка, показваме страницата за грешки при разработка съдържаща детайлна информация къде е възникнала грешката, а в случай че използваме продуктова среда използваме Error страница на която показваме на потребителя базова информация и контакти за съпорт.

Тъй като ASP.NET Core MVC проектите вече не използват web.config файлове, трябва да мигрираме конфигурацията от тези файлове в новия файл appsettings.json създаден от MVC темплейта. Отново, в случай че не използваме MVC темплейт, трябва да направим следните ръчни промени.

Трябва да добавим конфигурационните стойности в appsettings.json файла. Примерно за да мигрираме connection string-a за достъп до базата на приложението от web.config в appsettings.json трябва да го добавим, следвайки следният пример.

За да можем да прочетем и използваме конфигурационните стойности от appsettings.json файла, трябва да добавим следния конструктор за класа Statup.cs.

 

 

Основната разлика между appsettings.json файла и web.config файла, е че в web.config имаме дефинирани конфигурации в XML формат, докато в appsettings.json файла, те са дефинирани в json формат.

Нанимаме следната XML настройка в web.config.

Тази настройка, ще изглежда по идентичен начин в appsettings.json файла, но ще бъде в json формат.

В примерът по-горе е показана Smtp конфигурация. Ако искаме да прочетем тази конфигурация и на нейна база да създадем Smtp конфигурационнен обект, трябва първо да създадем клас в нашия проект отговарящ на този обект с съответните посочени в Smtp конфигурацията стойности.

След като създадем този клас, вече можем да заредим прочетената конфигурация в обект от този клас. За целта в метод ConfigureServices в клас Startup.cs трябва да добавим следния ред.

Подробни описания на видовете конфигурации достъпни в ASP.NET Core можем да прочетем тук – https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1&tabs=basicconfiguration

Използване на Dependency Injection в ASP.NET Core MVC

За да използваме Dependency Injection в ASP.NET Core MVC проекта разполагаме със следните варианти:

– Да използваме вградения в .NET Core, Inversion of Control (IOC) контейнер. Тази опция е полезна за напълно нови проекти .NET Core проекти.
– Да използваме специално разработени 3rd party IOC контейнери. Тази опция е полезна за проекти в които вече сме инвестирали време и ресурси и върху които извършваме миграция на съществуваща логика от .NET Framework към .NET Core. Този вариант използвахме и в нашия проект.

Все пак нека първо да разгледаме как се използва вграденият в .NET Core, IOC контейнер, в случай че решим да разработим чисто нов .NET Core проект.

Преди съществуването на .NET Core, единствения начин да добавим Dependency Injection в нашите приложения бе използвайки фреймуърци като Autofac, Ninject, StructureMap и други. Ако искаме да се запознаем в детайли с тези фреймуърци, можем да прочетем детайла информация ето тук за Autofac, за Ninject и за StructureMap.

В .NET Core обаче Dependency Injection играе основна роля и може да се конфигурира по следния начин в метод ConfigureServices в клас Startup.cs.

Когато получим уеб заявка към нашия контролер, тя ще бъде обработена използваки IOC контейнера.

При дефинирането на dependency-та трябва да бъде дефиниран и lifetime-а им. Lifetime дефиницията определя условията при които дадена услуга ще бъде създадена. По-долу са изброени lifetime дефинициите използвани от ASP.Net Dependency Injection фреймуърка.

  • Transient – услугата се създава всеки път когато е извикана.
  • Scoped – създава се веднъж в рамките на scope-a. Например, ако scope-ът ни е уеб заявката, то услугата се създава само веднъж за конкретна уеб заявка Това е най-срещаният случай, но може да имаме и други случаи, където scope-a ни да бъде примерно Azure Function услуга.
  • Singleton – създава се само за първата уеб заявка. Ако определена инстанция на дадената услуга е създадена по време на регистрацията и в контролера, тази инстанция ще се използва от всички методи, които използват услугата.

Използването на специално разработени Dependency Injection фреймуърци ни предоставя допълнители lifetime дефиниции и функционалности. Използването им може да се осъществи лесно, стига те да имплементират IServiceProvider интерфейса. Ако фреймуърците които сме избрали не имплементират този интерфейс, не е проблем да имплементираме интерфейса сами по следния начин.

Имплементацията на специално разработения 3rd party IOC контейнер представлява връщане на инстанция на контейнера в метод ConfigureServices в клас Startup.cs. По-долу е показан пример как това се реализира използвайки Autofac.

Миграция на модули в ASP.NET Core MVC

„Version control” клиентът ни разполагаше с няколко HTTP модула, които трябваше да мигрираме към .NET Core. Даден HTTP модул наподобява следния код.

HTTP модулите са част от pipeline-a на заявката и имат достъп до lifecycle събитията на тази заявка, т.е. те ни дават достъп до специфични данни за дадената заявка и ни позволяват да предприемем конкретни действия на базата на тези данни.

За да ги прехвърлим в .NET Core проекта, за всеки модул трябва да създадем .NET Core Middleware клас. Това е клас, който има Invoke метод приемащ за параметър HttpContext и връщащ Task.

Допълнително можем да добавим и helper клас, който да ни помогне с конфигурирането на middleware класа ни в Startup.cs класа. В този клас ще има метод, който да добавя middleware класа ни към pipeline-a на заявката. Услугите необходими на middleware класа се инжектират в конструктора му.

След като изпълним тези стъпки, конфигурирането на middleware класа в Startup.cs става с един ред в метод Configure, използвайки helper класа.

app.UseMyMiddleware();

В проекта ни разполагахме и с HTTP handler, който трябваше да мигрираме в новия ASP.NET Core MVC проект.

HTTP handler-а е процес, който се изпълнява при заявка към ASP.NET уеб приложение. Примерен HTTP handler e ASP.NET page handler-ът който обработва заявките към .aspx файловете.

В проекта ни имахме HTTP handler наподобяващ следния:

Следвайки примера с HTTP модулите, създадохме .NET Core middleware клас подобен на следния.

Единственото което трябва да направим в Startup.cs класа е да извикаме HTTP handler-a в метод Configure, по начин подобен на посочения по-долу за да може handler-ът да обработи заявката.

Заключение

В тази статия разгледахме, някои основни стъпки и добри практики при мигриране на ASP.NET MVC проект използващ .NET Framework, към ASP.NET MVC проект използващ .NET Core.

Един от най-важните етапи при миграцията е анализът на съвместимостта на съществуващия проект с .NET Core, и правилно извършен, този анализ може да ни спести доста време и усилия.

Но това бяха само основните стъпки на миграция. .NET Core платформата е изключително богата и позволява преизползването на повечето от функционалностите на ASP.NET MVC, но добавя и още много възможности.

В тази статия не можахме да разгледаме някои от по-специфичните теми за .NET Core, като Unit Testing, Deployment и т.н., но .NET Core Guide документацията е обширна и съдържа добро описание на тези теми.

Почти всичко необходимо, което ще ви трябва за да реализирате успешна миграция на ASP.NET MVC проект към .NET Core, или за разработка на изцяло нов ASP.NET Core MVC проект може да намерите тук.


Автор:
Евгени Дюлгеров

>>> Работи като Senior .NET Developer – Contractor в KPMG ITS, като официалното му работно място е в ScaleFocus;
>>>>>> Завършил  е бакалавър „Компютърни системи и технологии“ в ТУ-София, и в момента завършва магистратура „Управление на ресурси в предприятията“ в английския факултет на ТУ-София.
>>> Хобитата му са плуване, латино танци, играене на футбол на маса.
>>> Интересите му също са разнообразни – писане на проекти с нови технологии, разработка на freelance проекти през свободното време.


Стани част от потребителските групи на DEV.BG. Абонирай се и ще ти изпращаме информация за всичко, което предстои в групата.

Прочети още:
Serverless бекенд за трансформация на данни (Част 1)
Как можем да използваме Azure WebApps за решаване на проблеми, с които CRM Online не може да се справи

Share This