Готуємо ASP.NET Core: поговоримо про нестандартні підходи при роботі з уявленнями

Ми продовжуємо нашу колонку по темі ASP.NET Core публікацією від Дмитра Сікорського ( DmitrySikorsky — керівника компанії «Юбрейнианс» з України. У своїй черговій статті Дмитро розповідає про досвід нестандартної роботи з поданнями до ASP.NET Core. Попередні статті з колонки завжди можна прочитати за посиланням #aspnetcolumn — Володимир Юнев
останнім часом я багато працював над своїм модульним фреймворком для ASP.NET 5 (тепер вже ASP.NET Core 1.0). В рамках цього проекту довелося вирішувати різні завдання, і однією з них була робота з уявленнями, які перебувають або просто в нестандартних місцях, або взагалі поза основною складання веб-додатки. У цій статті я спробую розповісти, які у вас є варіанти, якщо вам необхідно щось подібне.


Подання в нестандартних місцях всередині основної збірки веб-додатки
Якщо раптом з якоїсь причини ваші уявлення виявилися поза належною їм папки Views (але, при цьому, все-таки залишилися всередині основного проекту програми), вам потрібно буде повідомити про це Razor'y. Якщо раніше для цього довелося б написати клас, похідний від RazorViewEngine, то зараз зробити це трохи простіше.

aspnetcolumngithubПорада! Ви можете спробувати все самостійно або завантаживши вихідний код з GitHub https://github.com/DmitrySikorsky/AspNet5Views.
Во-первых, реалізуємо інтерфейс IViewLocationExpander (нагадаю, що «{1}» у строкової константі буде замінено на ім'я контролера, а «{0}» – на ім'я дії):

public class CustomViewLocationExpander : IViewLocationExpander
{
public IEnumerable < string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable < string> viewLocations)
{
List<string> expandedViewLocations = new List < string>();

expandedViewLocations.AddRange(viewLocations);
expandedViewLocations.Add("/Views/SomeExtraFolder/{1}/{0}.cshtml");
return expandedViewLocations;
}

public void PopulateValues(ViewLocationExpanderContext context)
{
}
}

Потім «зареєструємо» примірник отриманого класу в методі ConfigureServices:

services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationExpanders.Add(new CustomViewLocationExpander());
}
);

Ось і все, тепер Razor візьме до відома наше нове розташування уявлень.

Подання у вигляді ресурсів в інших збірках
Насправді, я не знаю про переваги (але знаю про недоліки!) цього підходу перед наступним, але все-одно вважаю за необхідне про нього розповісти.

Щоб уявлення були поміщені в збірку в якості ресурсів, необхідно додати в project.json відповідного проекту наступний рядок (насправді, таким чином ресурси перетворюються не тільки подання):

«resource»: «Views/**»

Щоб такі подання були потім виявлені Razor'ом, необхідно реалізувати інтерфейс IFileProvider і присвоїти примірник отриманого класу відповідному властивості в методі ConfigureServices в основному проекті:

services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProvider = this.GetFileProvider(this.applicationBasePath);
}
);

Метод GetFileProvider створює екземпляр нашого класу CompositeFileProvider, який, по суті, просто об'єднує кілька різних провайдерів (в нашому випадку це фізичні файли в папці з основним проектом і ресурси із зазначених зборок).

Самий головний недолік даного підходу в тому, що ми не маємо можливості використовувати для типізації уявлень типи, визначені в складках, на які немає явних посилань в основному проекті. Наприклад, не можна використовувати типи, які визначені в проекті, в якому знаходяться самі уявлення. Це відбувається з-за того, що подання у вигляді ресурсів компілюються вже під час виконання і, відповідно, їх типи моделей не можуть бути знайдені. Це не проблема, якщо є можливість додати залежності явно, але якщо потрібно довантажувати складання динамічно, під час виконання, то зробити цього не можна. Наступний підхід вирішує цю проблему.

(Насправді, наскільки я зрозумів, це все-таки можна вирішити. Я обговорював це питання тут: https://github.com/aspnet/Mvc/issues/3413, але далі розмов справа поки не пішла.)

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

Як і в попередньому випадку, при використанні попередньо скомпільованих уявлень вам не потрібно копіювати їх на сервер при публікації, що значно прискорює цей процес (замість безлічі cshtml-файлів копіюється лише один dll-файл). Але, на відміну від уявлень у вигляді ресурсів, такі вистави, як зрозуміло з назви, компілюються на етапі складання проекту, а не під час виконання, що, по-перше, дозволяє заздалегідь виявити всі помилки в них, а по-друге, помітно економить час при запуску програми при зверненні до конкретних сторінок.

Щоб змусити компілятор компілювати подання, досить у відповідному проекті створити клас RazorPreCompilation, успадкувати його від RazorPreCompileModule, перевизначити в ньому метод EnablePreCompilation так, щоб він не поверне true, і в кінці кінців помістити його в папку \Compiler\PreProcess:

public class RazorPreCompilation : RazorPreCompileModule
{
protected override bool EnablePreCompilation(BeforeCompileContext context) => true;
}

В основному проекті програми, щоб повідомити Razor', що він повинен використовувати попередньо скомпільовані подання з інших збірок, необхідно просто передати набір цих збірок у відповідний метод:

services.AddMvc()
.AddPrecompiledRazorViews(
new Assembly[] { Assembly.Load(new AssemblyName("AspNet5Views.PrecompiledViews")) }
);

Цього достатньо, щоб все запрацювало.

Як я писав вище, завдяки тому, що уявлення компілюються на етапі складання проекту, ми маємо можливість типізувати їх типами, на які в основному проекті програми посилань немає.

Висновки
Загалом, все досить просто. У двох останніх підходів є суттєвий недолік, а саме, необхідність перезапуску всього програми для зміни, наприклад, навіть самої незначної деталі у верстці. Але, з іншого боку, це все-одно набагато зручніше, ніж копіювати подання з усіх частин програми в основний проект, якщо вже вам необхідно розділити програму на частини.

Я підготував невеликий тестовий проект, щоб можна було самостійно спробувати все, що описано в цій статті: https://github.com/DmitrySikorsky/AspNet5Views.

Авторам
Друзі, якщо вам цікаво підтримати колонку своїм власним матеріалом, то прошу написати мені на vyunev@microsoft.com для того, щоб обговорити всі деталі. Ми шукаємо авторів, які можуть цікаво розповісти про ASP.NET та інші теми.

Про автора
Сікорський Дмитро Олександрович
Компанія «Юбрейнианс» (http://ubrainians.com/)
Власник, керівник
DmitrySikorsky

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.