Ну, вот наше решение. Многие мелкие детали могут быть опущены, но общая идея здесь. Этот ответ может быть своего рода оффтопом к первоначальному вопросу, но он описывает общее решение проблемы.
Я попытаюсь объяснить ту часть, которая отвечает за простые пользовательские HTML-страницы, которые создаются пользователями во время выполнения и поэтому не могут иметь свой собственный контроллер / действие. Таким образом, маршруты должны быть либо каким-либо образом построены во время выполнения, либо быть «универсальными» с помощью пользовательского IRouteConstraint.
Прежде всего, давайте изложим некоторые факты и требования.
- У нас есть некоторые данные и некоторые метаданные о наших страницах, хранящиеся в БД;
- Мы не хотим заранее (гипотетически) создавать целый миллион маршрутов для всех существующих страниц (т. Е. При запуске приложения), потому что что-то может измениться во время приложения, и мы не хотим сталкиваться с продвижением изменений в глобальном масштабе. RouteCollection; * +1010 *
Итак, мы делаем это так:
1. PageController
Да, специальный контроллер, который отвечает за все наши контентные страницы. И есть единственное действие, которое Display(int id)
(на самом деле у нас есть специальный ViewModel в качестве параметра, но я использовал int id
для простоты.
Страница со всеми данными разрешается по идентификатору внутри этого Display()
метода. Сам метод возвращает либо ViewResult
(строго типизированный после PageViewModel
), либо NotFoundResult
в случае, если страница не найдена.
2. Пользовательская IRouteConstraint
Нам нужно где-то определить, ссылается ли фактически запрошенный пользователь URL на одну из наших пользовательских страниц. Для этого у нас есть специальный IsPageConstraint
, который реализует интерфейс IRouteConstraint
. В методе Match()
нашего ограничения мы просто вызываем наш PageRepository
, чтобы проверить, существует ли страница, соответствующая нашему запрошенному URL. У нас есть наш PageRepository, добавленный StructureMap. Если мы находим страницу, то добавляем этот параметр «id» (со значением) в словарь RouteData, и он автоматически связывается с PageController.Display(int id)
на DefaultModelBinder
.
Но для проверки нам нужен параметр RouteData. Где мы это получили? Вот идет ...
3. Отображение маршрута с параметром «поймать все»
Важное примечание: этот маршрут определен в самом конце списка отображений маршрутов, потому что он очень общий, а не конкретный. Сначала мы проверяем все наши явно определенные маршруты, а затем проверяем наличие Page
(которое легко изменить при необходимости).
Мы просто наносим на карту наш маршрут так:
routes.MapRoute("ContentPages",
"{*pagePath}",
new { controller = "Page", action = "Display" }
new { pagePath = new DependencyRouteConstraint<IsPageConstraint>() });
Стоп! Что это за вещь DependencyRouteConstraint
появилась в картографии? Ну, вот что делает трюк.
4. DependencyRouteConstraint класс
Это просто еще одна универсальная реализация IRouteConstraint
, которая принимает "real" IRouteConstraint
(IsPageConstraint) и разрешает его (данный TConstraint
) только при вызове метода Match()
. Он использует внедрение зависимостей, поэтому в нашем экземпляре IsPageConstraint
вставлены все фактические зависимости!
Наш DependencyRouteConstraint
затем просто вызывает dependentConstraint.Match()
, предоставляя все параметры, таким образом просто делегируя фактическое "соответствие" "реальному" IRouteConstraint.
Примечание: этот класс фактически зависит от ServiceLocator.
Резюме
Таким образом, у нас есть:
- Наши
Route
чистые и чистые;
- Единственный класс, который зависит от Service Locator, - это
DependencyRouteConstraint
;
- Любой пользовательский
IRouteConstraint
использует внедрение зависимостей при необходимости;
- ???
- PROFIT!
Надеюсь, это поможет.