- PVSM.RU - https://www.pvsm.ru -

ASP.NET Core RC2: встроенная поддержка модульности (application parts)

Будучи исторически погруженным в вопросы разработки модульных приложения на ASP.NET, первое что я сделал, когда вышел ASP.NET Core RC2 – постарался перевести на него свой модульный фреймворк ExtCore. И вот тут оказалось, что в новой версии все изменилось и старые подходы из RC1 больше не работают, зато появились новые интересные возможности, о которых я и хочу рассказать.

Если коротко, то разработка модульных приложений в RC2 очень упрощена. Благодаря новой возможности «части приложения» (application parts), вы легко можете разделить свой большой проект на несколько более мелких и затем свободно компоновать их. Особенно это удобно при работе с областями (areas), которые и так изолируют набор контроллеров, представлений и прочих ресурсов — каждую область теперь можно выделить в отдельный проект. Насколько я понял (в частности, из aspnet/Mvc#4089 [1]), реализация ориентирована именно на разделение большого проекта на маленькие и только в части MVC. Остальное все-таки придется писать самому.

Реализация

Для примера, создадим небольшое приложение и посмотрим, как все работает (предполагается, что вы уже зашли сюда [2] и установили все, что нужно). Итак, создаем проект:

ASP.NET Core RC2: встроенная поддержка модульности (application parts) - 1

На следующем шаге выбираем «Веб-приложение», чтобы Visual Studio создала для нас готовое к тестированию приложение:

ASP.NET Core RC2: встроенная поддержка модульности (application parts) - 2

Вот и все. Теперь запустим наше новое приложение:

ASP.NET Core RC2: встроенная поддержка модульности (application parts) - 3

Я не буду останавливаться на структуре проекта и на отличиях структуры от того, к чему мы привыкли в RC1. При желании, можно посмотреть вот это [3].

Теперь добавим еще один проект в наше решение, на этот раз библиотеку классов:

ASP.NET Core RC2: встроенная поддержка модульности (application parts) - 4

Т. к. мы хотим посмотреть на работу контроллера и представления из нашей сборки, добавим в файл project.json ссылку на MVC. Также нам необходимо, чтобы представления в этом проекте были добавлены в сборку в виде ресурсов. Это делается при помощи соответствующей настройки в разделе buildOptions файла project.json. В результате получим такой файл:

{
  "buildOptions": { "embed": [ "Views/**" ] },
  "dependencies": {
    "Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",
    "NETStandard.Library": "1.5.0-rc2-24027"
  },
  "frameworks": {
    "netstandard1.5": {
      "imports": "dnxcore50"
    }
  },
  "version": "1.0.0-*"
}

Теперь создадим в нашем проекте новый контроллер с единственным методом (для единообразия файл с классом контроллера желательно поместить в папку Controllers, хотя это и необязательно):

public class ModuleAController : Controller
{
  public ActionResult Index()
  {
    return this.View();
  }
}

Теперь в папке ViewsModuleA создадим представление Index.cshtml с таким содержимым, которое вам нравится.

Проект готов. Соберем его. В папке с проектом появится папка bin (как в прошлых версиях ASP.NET), а в ней — наша сборка. Осталось только рассказать о ней основному приложения.

Откроем класс Startup проекта нашего приложения и перейдем к методу ConfigureServices. Первым делом загрузим нашу сборку с контроллером и представлением:

Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(@"абсолютный путь к сборке");

Далее, добавим загруженную сборку в качестве части приложения в MVC:

services.AddMvc().AddApplicationPart(assembly);

Практически все. Если сейчас запустить приложение и перейти по адресу /modulea, мы получим исключение: InvalidOperationException: The view 'Index' was not found. Чтобы объяснить MVC, что искать представления необходимо и внутри нашей сборки, добавим соответствующий провайдер файлов в настройках Razor. Модифицируем предыдущую строчку кода, чтобы получилось вот так:

services.AddMvc().AddApplicationPart(assembly).AddRazorOptions(
  o =>
  {
    o.FileProviders.Add(new EmbeddedFileProvider(assembly, assembly.GetName().Name));
  }
);

Наше приложение, состоящее из двух частей, готово. Запустим его, перейдем по адресу /modulea:

ASP.NET Core RC2: встроенная поддержка модульности (application parts) - 5

Очень здорово. Еще в RC1 для этого потребовалось бы больше кода. Но этого достаточно только до тех пор, пока вы не захотите использовать строго типизированные представления. Если добавить класс модели вида в наш проект, и затем указать его в качестве модели для нашего представления, во время выполнения мы получим исключение: The type or namespace name 'ModuleA' does not exist in the namespace 'AspNetCoreApplicationParts'. Связано это с тем, что наша сборка не входит в набор сборок, в котором Razor ищет типы при компиляции представлений. К счастью, есть достаточно простой способ [4] это исправить. Кроме того, в ближайшем будущем в этом шаге не будет необходимости, т. к. сборки, добавленные как части приложения, будут участвовать в Razor-компиляции автоматически.

Модифицируем вызов функции AddRazorOptions, которую мы использовали на предыдущем шаге, таким образом:

.AddRazorOptions(
  o =>
  {
    o.FileProviders.Add(new EmbeddedFileProvider(assembly, assembly.GetName().Name));

    Action<RoslynCompilationContext> previous = o.CompilationCallback;

    o.CompilationCallback = c =>
    {
      if (previous != null)
      {
        previous(c);
      }

      c.Compilation = c.Compilation.AddReferences(reference);
    };
  }
);

Осталось объявить переменную reference где-то перед загрузкой сборки:

PortableExecutableReference reference = MetadataReference.CreateFromFile(@"абсолютный путь к сборке");

Вот и все. Теперь мы можем использовать нашу модель вида. Запустим приложение и перейдем по адресу /modulea:

ASP.NET Core RC2: встроенная поддержка модульности (application parts) - 6

Кстати, еще в RC1 можно было использовать предварительную компиляцию представлений и не иметь проблем с разрешением типов моделей видов во время выполнения. К сожалению, в RC2 предварительная компиляция не поддерживается (насколько я понял, из-за сложности реализации), но в будущем будет возвращена [5].

Результат

Пожалуй, application parts — именно то, чего давно не хватало ASP.NET. Я потратил много времени, чтобы добиться аналогичного результата в предыдущих версиях (еще до ASP.NET Core). Надеюсь, приведенного примера вполне достаточно, чтобы начать использовать эту возможность. И спасибо ребятам из нашего чата в Gitter, вместе с которыми мы разбирались с RC2.

Весь проект целиком (в немного упрощенном виде) доступен на GitHub [6].

Автор: DmitrySikorsky

Источник [7]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/net/121254

Ссылки в тексте:

[1] aspnet/Mvc#4089: https://github.com/aspnet/Mvc/issues/4089

[2] сюда: https://www.microsoft.com/net/core

[3] вот это: https://docs.asp.net/en/latest/migration/rc1-to-rc2.html

[4] достаточно простой способ: https://github.com/aspnet/Mvc/issues/4087#issuecomment-191899644

[5] в будущем будет возвращена: https://github.com/aspnet/Mvc/issues/3993

[6] GitHub: https://github.com/DmitrySikorsky/AspNetCoreApplicationParts

[7] Источник: https://habrahabr.ru/post/301336/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best