Я с новой версией ASP.NET Core с превью или ранней беты (уже не помню), успел привыкнуть. Если честно, мне не так важен формат этого файла. Каких-либо проблем или сложностей не вызывает.
За последние много лет я сделал больше сотни сайтов на ASP.NET и продолжаю. Мне очень нравится эта платформа и среда разработки. Думаю, это дело вкуса и зависит от задачи. Для меня C# — лучший язык (хотя я работаю со многими, вроде Java и Objective C). Лично для меня появление кроссплатформенной ASP.NET — по-настоящему важное событие, которому я чрезвычайно рад.
Мне нравится MEF, я использовал его для разработки модульных веб-приложений на предыдущей версии ASP.NET и MVC. Теперь, насколько я понимаю, он позиционируется как решение для настольных приложений (вроде Visual Studio, где он и применяется). Он не кросс-платформенный, да и вообще, уже не вписывается в новую концепцию, поэтому, думаю, и остался за бортом.
Согласен, что, пожалуй, с RC1 погорячились. Я тоже не ожидал, что много часов моей работы в итоге пропадет, т. к. все так круто изменилось. Но все-таки понять, что реализация не оптимальная и внести изменения на раннем этапе это лучше, чем просто развивать компромиссный вариант.
Вдруг еще вспомнил, как мучились люди (и на какие шли ухищрения), которые хотели вынести область (area) в отдельный проект. В итоге, большинство простых решений были очень сомнительным, вроде копирования проекта с областью в папку Areas основного проекта и так далее. А теперь это делается совершенно просто. Также я помню сколько часов у меня ушло, чтобы заставить Razor в MVC5 компилировать представления, которые использовали модели видов из внешних сборок. В общем, это действительно здорово, что теперь все это в прошлом.
Не совсем понятно, плюсы в сравнении с чем. Если с предыдущей версией (RC1 и ранее), то основной плюс — значительное уменьшение объема кода, который необходимо написать, чтобы использовать контроллеры и представления из других сборок, на которые нет явных ссылок в project.json. Вы просто загружаете сборку и говорите MVC брать это все оттуда вызовом пары функций (а в скором будущем — одной функции, насколько я понял). Например, из моего проекта можно будет удалить вот этот класс, а также и вот этот. Если говорить о преимуществах модульных приложений вообще, то в крупном проекте очень удобно, когда различные разработчики (или команды) работают на различных проектах (например, слой доступа к данным, слой сервисов, фронтенд, бекенд). Не говоря уже о различных плагинах и т. п.
Ну в общем да, на любителя. Мне нравится для достижения состояния "тонких контроллеров" использовать модели видов и их билдеры/мапперы. Сложные модели видов разбивать на более простые и вызывать билдеры по цепочке. Тогда методы в контроллерах тоже в одну строку.
Да. Но я об этом и говорю, взгляните на слой сервисов в этом примере. Он практически полностью состоит из методов вроде:
public Category GetCategory(int id)
{
var category = categorysRepository.GetById(id);
return category;
}
Т. е. в большинстве случаев, где нет какой-то действительно сложной предметной области, слой сервисов это, как по мне, лишняя прослойка, идентичная слою единицы работы с репозиториями по функциональности, но находящаяся над ним. Еще одна параллельная иерархия интерфейсов/реализаций.
Насчет выделения цели — согласен, это правильная мысль. И картинки тоже. Все приходит с опытом.
Насчет сценариев использования. Насколько я понимаю, это нечто вроде сервисного слоя? Т. е. контроллер обращается к сервису по какому-то методу предметной области, тот обращается к репозиториям (одному или нескольким), те, в свою очередь, уже к ORM и слою хранилища или источника данных. Правильно я понял? Если так, то я не использовал сервисный слой, чтобы не добавлять эту еще одну прослойку там, где в ней нет потребности. А объемные специфические задачи предметной области я обычно переношу на некие классы-хелперы, менеджеры и так далее.
На счет ExtCore.WebApplication.Startup. Этот класс необходим, чтобы отнаследовавшись от него получить весь необходимый функционал (поиск, подготовка расширений и прочее). Имена одинаковые для удобства (т. к. своим классом вы дополняете базовый). В своем классе вы можете не переопределять функции ConfigureServices и Configure, если добавить туда нечего. Т. е. это просто базовый класс.
На счет структуры расширений. Да, как-то я упустил этот момент. Исхожу из следующих идей. Чтобы проекты одного расширения не выглядели разрознено, логично, чтобы названия проектов начинались названием расширения. Если расширение большое (как в случае с CMS), такие куски, как фронтенд и бекенд, лучше разделять на отдельные проекты. Так с ними удобнее работать, особенно в команде. Также, при необходимости можно подключить только фронтенд и не подключать бекенд. Что касается данных. Модели удобно выносить в отдельный проект, т. к. в больших расширениях (да и вообще, приложениях) ссылки на них могут быть нужны в различных проектах. Кроме того, иногда проекту нужны только описания моделей и не нужна работа с базой. Мне больше нравится, когда добавив ссылку на проект с моделями я не получу ничего лишнего (вроде реализации работы с БД). Абстракции репозиториев лежат также в отдельном проекте, т. к. именно через них производится вся работа с данными, без знания о конкретных реализациях для конкретных хранилищ. Ну и, соответственно, конкретные реализации лежат каждая в своем проекте, чтобы можно было использовать независимо ту или иную реализацию для того или иного хранилища. Также можно в любой момент добавить проек-реализацию нового хранилища и ничего не придется переделывать благодаря такой структуре.
Т. е. если вам необходимо подключить расширение, вы подключаете основной проект и выбранную конкретную реализацию репозиториев (например, для SQLite, если расширение вообще работает с хранилищем). Возможно, эту идею хорошо проиллюстрирует эта ссылка.
Буду рад идеям по улучшению этой структуры.
Спасибо за комментарий! CMS делал не исходя из желания сделать "что-то свеженькое", а из-за необходимости именно в таком инструменте для работы. На предыдущей версии есть множество проектов, сейчас решил переписать, сделать модульно и кроссплатформенно, раз уж такая возможность (я имею в виду кроссплатформенность) появилась на моей любимой платформе .NET. Может быть это старомодно, но да, мне нравится, что ASP.NET-приложение работает на Linux и Mac. Даже без стороннего веб-сервера.
Что касается фреймворка — он, кстати, позволяет загружать не только dll, но и другие штуки, вроде NuGet-пакетов или просто проектов в исходниках. Было бы интересно взглянуть, как можно было бы решить задачу иначе. Не видел пока что ничего подходящего. На счет скорости — "жуткого торможения" не наблюдаю, но без сомнения, некое падение производительности у модульного проекта по отношению к "цельному" конечно же будет. Но это цена, которую придется заплатить за гибкость и удобство. В общем, буду благодарен, если приведете больше конкретики. Спасибо!
Не только видел, но и использовал в собственном решении. Безо всяких дополнительных действий или сложностей. В моем решении все проекты были физически размещены на одном уровне в файловой системе, а в решении находились в различных папках. Может быть это имеет значение.
Task Runner Explorer в выпадающем списке отображает все проекты, где есть gulpfile.js, и позволяет переключаться между ними в любой момент. Т. е. вы сможете выполнять задания из всех своих gulp-файлов.
По сути, MEF делает за вас поиск сборок, извлечение из них типов и создание экземпляров объектов, поэтому это действительно больше, чем просто DI. Но все-таки для создания модульного веб-приложения этого недостаточно. Необходимо определиться, как подключать контроллеры и представления, как работать с данными и так далее. Так что это всего лишь инструмент, а не решение. Думаю, если задействовать его (MEF) в нашем тестовом примере, он не слишком изменится архитектурно.
Я считаю, что неправильно складывать и контроллеры, и классы, взаимодействующие с хранилищем, в один проект. В крупном проекте это приведет к путанице и значительно усложнит командную работу. Также мне нравится, что даже если на самом низком уровне нет поддержки определенного хранилища, можно легко ее добавить, взяв за основу (в нашем упрощенном случае) AspNet5ModularApp.Data.EF.Sqlite и просто добавив еще один проект, не затрагивая ничего больше. (Например, в Платформусе я использую и SQLite, и MS SQL Server.)
Согласен, что работа с данными это часть функций ядра приложения. По сути, AspNet5ModularApp.Models.Abstractions, AspNet5ModularApp.Data.Abstractions и AspNet5ModularApp.Data.EF.Sqlite и есть частью ядра, которую используют все расширения, которым необходима работа с данными.
Что касается MEF – мне очень понравилась эта штука, я как раз построил предыдущий Платформус на ее базе. В новой версии использовать ее не стал. Насколько я понял, она не поддерживает dnxcore50 (по крайней мере, не поддерживала на тот момент, когда я с этим разбирался), а для меня это крайне важно. Кроме того, в ASP.NET 5 уже встроен DI, работать с которым удобно.
Т. е. в большинстве случаев, где нет какой-то действительно сложной предметной области, слой сервисов это, как по мне, лишняя прослойка, идентичная слою единицы работы с репозиториями по функциональности, но находящаяся над ним. Еще одна параллельная иерархия интерфейсов/реализаций.
Насчет сценариев использования. Насколько я понимаю, это нечто вроде сервисного слоя? Т. е. контроллер обращается к сервису по какому-то методу предметной области, тот обращается к репозиториям (одному или нескольким), те, в свою очередь, уже к ORM и слою хранилища или источника данных. Правильно я понял? Если так, то я не использовал сервисный слой, чтобы не добавлять эту еще одну прослойку там, где в ней нет потребности. А объемные специфические задачи предметной области я обычно переношу на некие классы-хелперы, менеджеры и так далее.
На счет структуры расширений. Да, как-то я упустил этот момент. Исхожу из следующих идей. Чтобы проекты одного расширения не выглядели разрознено, логично, чтобы названия проектов начинались названием расширения. Если расширение большое (как в случае с CMS), такие куски, как фронтенд и бекенд, лучше разделять на отдельные проекты. Так с ними удобнее работать, особенно в команде. Также, при необходимости можно подключить только фронтенд и не подключать бекенд. Что касается данных. Модели удобно выносить в отдельный проект, т. к. в больших расширениях (да и вообще, приложениях) ссылки на них могут быть нужны в различных проектах. Кроме того, иногда проекту нужны только описания моделей и не нужна работа с базой. Мне больше нравится, когда добавив ссылку на проект с моделями я не получу ничего лишнего (вроде реализации работы с БД). Абстракции репозиториев лежат также в отдельном проекте, т. к. именно через них производится вся работа с данными, без знания о конкретных реализациях для конкретных хранилищ. Ну и, соответственно, конкретные реализации лежат каждая в своем проекте, чтобы можно было использовать независимо ту или иную реализацию для того или иного хранилища. Также можно в любой момент добавить проек-реализацию нового хранилища и ничего не придется переделывать благодаря такой структуре.
Т. е. если вам необходимо подключить расширение, вы подключаете основной проект и выбранную конкретную реализацию репозиториев (например, для SQLite, если расширение вообще работает с хранилищем). Возможно, эту идею хорошо проиллюстрирует эта ссылка.
Буду рад идеям по улучшению этой структуры.
Что касается фреймворка — он, кстати, позволяет загружать не только dll, но и другие штуки, вроде NuGet-пакетов или просто проектов в исходниках. Было бы интересно взглянуть, как можно было бы решить задачу иначе. Не видел пока что ничего подходящего. На счет скорости — "жуткого торможения" не наблюдаю, но без сомнения, некое падение производительности у модульного проекта по отношению к "цельному" конечно же будет. Но это цена, которую придется заплатить за гибкость и удобство. В общем, буду благодарен, если приведете больше конкретики. Спасибо!
Согласен, что работа с данными это часть функций ядра приложения. По сути, AspNet5ModularApp.Models.Abstractions, AspNet5ModularApp.Data.Abstractions и AspNet5ModularApp.Data.EF.Sqlite и есть частью ядра, которую используют все расширения, которым необходима работа с данными.
Что касается MEF – мне очень понравилась эта штука, я как раз построил предыдущий Платформус на ее базе. В новой версии использовать ее не стал. Насколько я понял, она не поддерживает dnxcore50 (по крайней мере, не поддерживала на тот момент, когда я с этим разбирался), а для меня это крайне важно. Кроме того, в ASP.NET 5 уже встроен DI, работать с которым удобно.