Мы продолжаем нашу колонку по теме ASP.NET5 публикацией от Станислава Бояринцева ( masterL) — разработчика корпоративных веб-систем из компании ItWebNet. В этой статье Станислав очень подробно и интересно рассказывает о механизме маршрутизации в ASP.NET5. Предыдущие статьи из колонки всегда можно прочитать по ссылке #aspnetcolumn — Владимир Юнев


Как была организована система маршрутизации до ASP.NET 5


Маршрутизация до ASP.NET 5 осуществлялась с помощью ASP.NET модуля UrlRoutingModule. Модуль проходил через коллекцию маршрутов (как правило объектов класса Route) хранящихся в статическом свойстве Routes класса RouteTable, выбирал маршрут, который подходил под текущий запрос и вызывал обработчик маршрута, который хранился в свойстве RouteHandler класса Route — каждый зарегистрированный маршрут мог иметь собственный обработчик. В MVC-приложении этим обработчиком был MvcRouteHandler, который брал на себя дальнейшую работу с запросом.

Маршруты в коллекцию RouteTable.Routes мы добавляли в процессе настройки приложения.

Типичный код настройки системы маршрутизации в MVC приложении:

RouteTable.Routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Где MapRoute — extension-метод, объявленный в пространстве имен System.Web.Mvc, который добавлял в коллекцию маршрутов в свойстве Routes новый маршрут используя MvcRouteHandler в качестве обработчика.

Мы могли бы сделать это и самостоятельно:

RouteTable.Routes.Add(new Route(
    url: "{controller}/{action}/{id}",
    defaults: new RouteValueDictionary(new { controller = "Home", action = "Index", id = UrlParameter.Optional }),
    routeHandler: new MvcRouteHandler())
);

Как организована система маршрутизации в ASP.NET 5: Короткий вариант


ASP.NET 5 больше не использует модули, для обработки запросов используются «middleware» введенные в рамках перехода на OWIN — «Open Web Interface» — позволяющей запускать ASP.NET 5 приложения не только на сервере IIS.

Поэтому сейчас маршрутизация осуществляется с помощью RouterMiddleware. Весь проект реализующий маршрутизацию можно загрузить с github. В рамках этой концепции запрос передается от одного middleware к другому, в порядке их регистрации при старте приложения. Когда запрос доходит до RouterMiddleware оно сравнивает подходит ли запрашиваемый Url адрес для какого-нибудь зарегистрированного маршрута, и если подходит, вызывает обработчик этого маршрута.

Как организована система маршрутизации в ASP.NET 5: Длинный вариант


Для того, чтобы разобраться как система маршрутизации работает, давайте подключим ее к пустому проекту ASP.NET 5.

  1. Cоздайте пустой проект ASP.NET 5 (выбрав Empty Template) и назовите его «AspNet5Routing».
  2. Добавляем в зависимости («dependencies») проекта в файле project.json «Microsoft.AspNet.Routing»:

    "dependencies": {
        "Microsoft.AspNet.Server.IIS": "1.0.0-beta5",
        "Microsoft.AspNet.Server.WebListener": "1.0.0-beta5",
        "Microsoft.AspNet.Routing": "1.0.0-beta5"
      },
    

  3. В файле Startup.cs добавляем использование пространства имен Microsoft.AspNet.Routing:

    using Microsoft.AspNet.Routing;
    

  4. Добавляем необходимые сервисы (сервисы, которые использует в своей работе система маршрутизации) в методе ConfigureServices() файла Startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRouting();
    }
    

  5. И наконец настраиваем систему маршрутизации в методе Configure() файла Startup.cs:

    public void Configure(IApplicationBuilder app)
    {
        var routeBuilder = new RouteBuilder();
        routeBuilder.DefaultHandler = new ASPNET5RoutingHandler();
        routeBuilder.ServiceProvider = app.ApplicationServices;
        routeBuilder.MapRoute("default", "{controller}/{action}/{id}");
        app.UseRouter(routeBuilder.Build());
    }
    

Взято из примера в проекте маршрутизации.

Разберем последний шаг подробнее:

var routeBuilder = new RouteBuilder();
routeBuilder.DefaultHandler = new ASPNET5RoutingHandler();
routeBuilder.ServiceProvider = app.ApplicationServices;

Создаем экземпляр RouteBuilder и заполняем его свойства. Интерес вызывает свойство DefaultHandler с типом IRouter — судя по названию оно должно содержать обработчик запроса. Я помещаю в него экземпляр ASPNET5RoutingHandler — придуманного мною обработчика запросов, давайте создадим его:

using Microsoft.AspNet.Routing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;

namespace AspNet5Routing
{
    public class ASPNET5RoutingHandler : IRouter
    {
        public VirtualPathData GetVirtualPath(VirtualPathContext context)
        {

        }

        public async Task RouteAsync(RouteContext context)
        {
            await context.HttpContext.Response.WriteAsync("ASPNET5RoutingHandler work");
            context.IsHandled = true;
        }
    }
}

Интерфейс IRouter требует от нас только два метода GetVirtualPath и RouteAsync.

Метод GetVirtualPath — нам знаком из предыдущих версий ASP.NET он был в интерфейсе класса RouteBase от которого наследовался класс Route представляющий собой маршрут. Этот метод отвечал за построение Url (например, когда мы вызывали метод ActionLink: Html.ActionLink(«link», «Index»)).

А в методе RouteAsync — мы обрабатываем запрос и записываем результат обработки в Response.

Следующая строка метода Configure:

routeBuilder.MapRoute("default", "{controller}/{action}/{id}");

Как две капли воды, похожа на использование метода MapRoute в MVC 5, его параметры — название добавляемого маршрута и шаблон с которым будет сопоставляться запрашиваемый Url.

Сам MapRoute() также как и в MVC 5 — extension-метод, а его вызов в итоге сводится к Созданию экземпляра класса TemplateRoute и добавлению его в коллекцию Routes нашего объекта RouteBuilder:

routeBuilder.Routes.Add(new TemplateRoute(routeCollectionBuilder.DefaultHandler,
                                                name, // в нашем случае передается "default"
                                                template, // в нашем случае передается "{controller}/{action}/{id}"
                                                ObjectToDictionary(defaults),
                                                ObjectToDictionary(constraints),
                                                ObjectToDictionary(dataTokens),
                                                inlineConstraintResolver));

Что интересно свойство Routes — это коллекция IRouter, то есть TemplateRoute тоже реализует интерфейс IRouter, как и созданный нами ASPNET5RoutingHandler, кстати, он передается в конструктор TemplateRoute.

И наконец последняя строчка:

app.UseRouter(routeBuilder.Build());

Вызов routeBuilder.Build() — создает экземпляр класса RouteCollection и добавляет в него все элементы из свойства Route класса RouteBuilder.

А app.UseRouter() — оказывается extension-методом, который на самом деле, подключает RouterMiddleware в pipeline обработки запроса, передавая ему созданный и заполненный в методе Build() объект RouteCollection.

public static IApplicationBuilder UseRouter([NotNull] this IApplicationBuilder builder, [NotNull] IRouter router)
{
    return builder.UseMiddleware<RouterMiddleware>(router);
}

И судя по конструктору RouterMiddleware:

public RouterMiddleware(
    RequestDelegate next,
    ILoggerFactory loggerFactory,
    IRouter router)

Объект RouteCollection тоже реализует интерфейс IRouter, как и ASPNET5RoutingHandler c TemplateRoute.

Итого у нас получилась следующая матрешка:

Наш обработчик запроса ASPNET5RoutingHandler упакован в TemplateRoute, сам TemplateRoute или несколько экземпляров TemplateRoute (если бы мы несколько раз вызвали метод MapRoute()) упакованы в RouteCollection, а RouteCollection передан в конструктор RouterMiddleware и сохранен в нем.

На этом процесс настройки системы маршрутизации завершен, можно запустить проект, перейти по адресу: "/Home/Index/1" и увидеть результат: «ASPNET5RoutingHandler work».

Ну и кратко пройдемся по тому, что происходит, с системой маршрутизации во время входящего запроса:

Когда очередь доходит до RouterMiddleware, в списке запускаемых middleware, оно вызывает метод RouteAsync() у сохраненного экземпляра IRouter — это объект класса RouteCollection.

RouteCollection в свою очередь проходит по сохраненным в нем экземплярам IRouter — в нашем случае это будет TemplateRoute и вызывает у них метод RouteAsync().

TemplateRoute проверяет соответствует ли запрашиваемый Url, его шаблону (передавали в конструкторе TemplateRoute: "{controller}/{action}/{id}") и если совпадает, вызывает хранящийся в нем экземпляр IRouter — которым является наш ASPNET5RoutingHandler.

Подключаем систему маршрутизации к MVC приложению


Теперь давайте посмотрим как связывается MVC Framework с системой маршрутизации.

Снова создадим пустой проект ASP.NET 5 используя Empty шаблон.

  1. Добавляем в зависимости («dependencies») проекта в файле project.json «Microsoft.AspNet.Mvc»:

    "dependencies": {
        "Microsoft.AspNet.Server.IIS": "1.0.0-beta5",
        "Microsoft.AspNet.Server.WebListener": "1.0.0-beta5",
        "Microsoft.AspNet.Mvc": "6.0.0-beta5"
      },
    

  2. В файле Startup.cs добавляем использование пространства имен Microsoft.AspNet.Builder:

    using Microsoft.AspNet.Builder;
    

Нужные нам extensions-методы для подключения MVC находятся в нем.

  1. Добавляем сервисы, которые использует в своей работе MVC Framework: в методе ConfigureServices() файла Startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }
    

  2. Настраиваем MVC приложение в методе Configure() файла Startup.cs:

Нам доступны три разных метода:

1.

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvc()
    }

2.

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvcWithDefaultRoute()
    }

3.

    public void Configure(IApplicationBuilder app)
    {
        return app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

Давайте сразу посмотрим реализацию этих методов:

Первый метод:

    public static IApplicationBuilder UseMvc(this IApplicationBuilder app)
    {
        return app.UseMvc(routes =>
        {
        });
    }

Вызывает третий метод, передавая делегат Action<IRouteBuilder>, который ничего не делает.

Второй метод:

    public static IApplicationBuilder UseMvcWithDefaultRoute(this IApplicationBuilder app)
    {
        return app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

Тоже вызывает третий метод, только в делегате Action<IRouteBuilder> добавляется дефолтный маршрут.

Третий метод:

    public static IApplicationBuilder UseMvc(
        this IApplicationBuilder app,
        Action<IRouteBuilder> configureRoutes)
    {
        MvcServicesHelper.ThrowIfMvcNotRegistered(app.ApplicationServices);

        var routes = new RouteBuilder
        {
            DefaultHandler = new MvcRouteHandler(),
            ServiceProvider = app.ApplicationServices
        };

        configureRoutes(routes);

        // Adding the attribute route comes after running the user-code because
        // we want to respect any changes to the DefaultHandler.
        routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(
            routes.DefaultHandler,
            app.ApplicationServices));

        return app.UseRouter(routes.Build());
    }

Делает тоже самое, что и мы в предыдущем разделе при регистрации маршрута для своего обработчика, только устанавливает в качестве конечного обработчика экземпляр MvcRouteHandler и делает вызов метода CreateAttributeMegaRoute — который отвечает за добавление маршрутов устанавливаемых с помощью атрибутов у контроллеров и методов действий (Attribute-Based маршрутизация).

Таким образом все три метода, будут включать в наше приложение Attribute-Based маршрутизацию, но кроме этого, вызов второго метода будет добавлять дефолтный маршрут, а третий метод, позволяет задать любые нужные нам маршруты передав их с помощью делегата (Convention-Based маршрутизация).

Convention-Based маршрутизация


Как я уже писал выше — настраивается с помощью вызова метода MapRoute() — и процесс использования этого метода не изменился со времен MVC 5 — в метод MapRoute() мы можем передать имя маршрута, его шаблон, значения по-умолчанию и ограничения.

routeBuilder.MapRoute("regexStringRoute", //name
                      "api/rconstraint/{controller}", //template
                      new { foo = "Bar" }, //defaults
                      new { controller = new RegexRouteConstraint("^(my.*)$") }); //constraints

Attribute-Based маршрутизация


В отличии от MVC 5, где маршрутизацию с помощью атрибутов, нужно было специально включать, в MVC 6 она включена по-умолчанию.

Следует также помнить, что маршруты определяемые с помощью атрибутов, имеют приоритет при поиске совпадений и выборе подходящего маршрута (по-сравнению с convention-based маршрутами).

Для задания маршрута нужно использовать атрибут Route как у методов действий, так и у контроллера (в MVC 5 для задания маршрута у контроллера использовался атрибут RoutePrefix).

[Route("appointments")]
public class Appointments : ApplicationBaseController
{
    [Route("check")]
    public IActionResult Index()
    {
        return new ContentResult
        {
            Content = "2 appointments available."
        };
    }
}

В итоге данный метод действия будет доступен по адресу: "/appointments/check".

Настройка системы маршрутизации


В ASP.NET 5 появился новый механизм настройки сервисов называющийся OptionsGitHub проекта. Он позволяет произвести некоторые настройки системы маршрутизации.

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

Для настройки системы маршрутизации используется класс RouteOptions.

Для удобства нам доступен метод расширения ConfigureRouting:

public void ConfigureServices(IServiceCollection services)
{
    services.ConfigureRouting(
        routeOptions =>
        {
            routeOptions.LowercaseUrls = true; // генерация url в нижнем регистре
            routeOptions.AppendTrailingSlash = true; // добавление слеша в конец url
        });
}

«За кулисами» он просто делает вызов метода Configure передавая в него делегат Action<RouteOptions>:

public static void ConfigureRouting(
    this IServiceCollection services,
    Action<RouteOptions> setupAction)
{
    if (setupAction == null)
    {
        throw new ArgumentNullException(nameof(setupAction));
    }

    services.Configure(setupAction);
}

Шаблон маршрута


Принципы работы с шаблоном маршрута остались теми же, что были и в MVC 5:

  • Сегменты адреса разделены слешем: firstSegment/secondSegment.
  • Константной часть сегмента считается, если она не окаймлена фигурными скобками, соответствие такого маршрута запрашиваемому Url, происходит только если в адресе присутствуют точно такие же значения: firstSegment/secondSegment — такой маршрут соответствует только адресу вида: siteDomain/firstSegment/secondSegment.
  • Переменные части сегмента берутся в фигурные скобки: firstSegment/{secondSegment} — шаблон будет соответствовать любым двух сегментным адресам, где первый сегмент: «firstSegment», а второй сегмент может быть любым набором символов (кроме слеша — так как это будет обозначать начало третьего сегмента):

    "/firstSegment/index"

    "/firstSegment/index-2"

  • Ограничения для переменной части сегмента, как это следует из названия — ограничивают допустимые значения переменного сегмента и задаются после символа ":". На одну переменную часть можно наложить несколько ограничений, параметры передаются с использованием круглых скобок: firstSegment/{secondSegment:minlength(1):maxlength(3)}. Строковое обозначение ограничений можно посмотреть в методе GetDefaultConstraintMap() класса RouteOptions.
  • Для того, чтобы сделать последний сегмент «жадным», так что он будет поглощать всю оставшуюся строку адреса, нужно использовать символ *: {controller}/{action}/{*allRest} — будет соответствовать как адресу: "/home/index/2", так и адресу: "/home/index/2/4/5".

Но в ASP.NET 5 шаблон маршрута получил некоторые дополнительные возможности:

  1. Возможность задавать прямо в нем значения по-умолчанию для переменных частей маршрута: {controller=Home}/{action=Index}.
  2. Задавать не обязательность переменной части сегмента с помощью символа ?: {controller=Home}/{action=Index}/{id?}.

Также при использовании шаблона маршрута в атрибутах, произошли изменения:

При настройке маршрутизации через атрибуты, к параметрам обозначающим контроллер и метод действия, теперь следует обращаться беря их в квадратные скобки и используя слова «controller» и «action»: "[controller]/[action]" — и использовать их можно только в таком виде — не разрешаются ни значения по-умолчанию, ни ограничения, ни опциональность, ни жадность.

То есть, разрешается:

Route("[controller]/[action]/{id?}")
Route("[controller]/[action]")

Можно использовать их по отдельности:

Route("[controller]")
Route("[action]")

Не разрешаются:

Route("{controller}/{action}")
Route("[controller=Home]/[action]")
Route("[controller?]/[action]")
Route("[controller]/[*action]")

Общая схема шаблона маршрута выглядит так:

constantPart-{variablePart}/{paramName:constraint1:constraint2=DefaultValue?}/{*lastGreedySegment}

Заключение


В этой статье, мы пробежались по системе маршрутизации ASP.NET 5, посмотрели как она организована и подключили ее к пустому проекту ASP.NET 5, используя свой обработчик маршрута. Разобрали способы ее подключения к MVC приложению и настройке с помощью механизма Опций. Остановились на изменениях, которые произошли в использовании Attribute-Based маршрутизации и шаблоне маршрута.

Авторам


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

Об авторе


Бояринцев Станислав Александрович
Ведущий программист .NET в ItWebNet, город Киров
masterL

.NET программист с 4 годами стажа. Занимается разработкой корпоративных веб систем. Сфера профессиональных интересов: ASP.NET MVC.
Блог: boyarincev.net
Ваше отношение к новому подходу к маршрутизации в ASP.NET5

Проголосовало 136 человек. Воздержалось 37 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Комментарии (14)


  1. SychevIgor
    01.10.2015 16:51

    Хорошая статья!

    Хотя, в статье не хватает ответа на вопрос- как тестировать(unit test-ами, т.к. как тестить через вызов из браузера-fiddler всем понятно и так.)
    Не в смысле, что я не знаю, а в смысле- в статье было полезно рассказать


    1. masterL
      01.10.2015 17:52
      +1

      Спасибо.
      Согласен, вопрос тестирования надо было бы затронуть, но я совсем забыл о нем.


      1. XaocCPS
        02.10.2015 10:12

        Хорошая тема для следующей статьи :-)


  1. RGB
    01.10.2015 18:51
    -1

    rails -> express


  1. vladimirkolyada
    01.10.2015 20:43

    Что с поиском подходящего маршрута по скорости? В MVC ASP.NET «старом» после 500 маршрутов уже начинаются проблемы. Полностью статические пути из разряда Friendly URL задавать вообще не вариант, приходится изобретать костыли.


    1. SychevIgor
      01.10.2015 22:37

      Я думаю на performance до RC1 в ноябре тестировать смысла нет.
      А что вызвало необходимость 500 роутов создать? Не праздный интерес, хочется понять сценарий.

      Для MVC(не web API) как-то представить тяжело. Для Web API- в restfull охотно верю


      1. vladimirkolyada
        02.10.2015 09:00
        +1

        Легко, если вы мне подскажете решение окромя: «ну создай отдельные роуты с параметрами (что я сейчас и делаю)», то я даже не знаю, как благодарить.
        Что необходимо:
        Есть набор страниц, в виде иерархии. Все пути url на сайте должны быть так же иерархично выстроены. Некоторые страницы могут иметь фильтрационные параметры, но они не включаются в иерархию. Роутинг должен меняться пользователем и быть динамическим.
        Пример:
        Главная — "/"
        Категория — «Категория»
        Подкатегория — «Подкатегория»
        Необходимый результат: Для страницы подкатегория ссылка "/Категория/Подкатегория/" и так далее.
        Результат:
        1. Данный пример легко реализовать, если засунуть в таблицу маршрутизации прямо целиком ссылки, статично. Но, после 500 начинаются проблемы со скоростью поиска роута. При 10000 все умирает на секунды. Для чего же надо все это засовывать в маршруты? — Потому что по адресу мне всегда нужно узнавать идентификатор страницы, то самое наименование, какие-то параметры (допустим Title и Description страницы) и так далее. В этом варианте пишем просто параметр, при инициализации роутинга, вида Id = IdFromDatabase и все, потом его выгребаем везде, где надо. Но, это плохой вариант
        2. Данный пример можно реализовать с построением маршутов с параметрами, классических. Т.е. Берем и делаем маршрут вида: /Категория/{subcategory}/, уменьшаем резко количество маршрутов. Но, сталкиваемся с тем, что мы больше не можем использовать id как параметр, ведь у нас 1 маршрут на n страницы, причем n принадлежит N. И тут начинается самое интересное, раз мы хотим универсально получать этот идентификатор и данные по нему, то нам нужно каким-то образом у маршрута знать, по каким параметрам искать в базе страницу. В этом случае это subcategory, в другом случае это category, в третьем это вообще что-то иное, да и больше 1 параметра может быть. Получаем достаточно сложную логику, как по мне. Конечно, она будет быстрее работать, там не будет простого поиска сверху — вниз по таблице маршрутизации. Но почему нельзя ускорить поиск самого MVC.


        1. masterL
          02.10.2015 10:41

          Если я правильно понял ваш кейс — то вам нужен всегда правильно заполненный параметр id в RouteData.
          Я думаю вам нужно идти с другой стороны:
          Хранить соответствие «данные маршрута => Id» (Категория, Субкатегория => Id) в какой-нибудь статической коллекции.
          А Id заполнять в RouteData в какой-то момент до того как оно вам понадобиться, поиском по этой статической коллекции, исходя из того, что система маршрутизации вам уже заполнит все необходимые параметры для поиска распарсив маршрут.

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


          1. vladimirkolyada
            02.10.2015 11:18

            Вы мне описали 2 мой вариант. Да, но при этому алгоритмы поиска нетривиальные + работа с маршрутизацией вылазит на уровень пользователя, а ему мои {param} непонятны чуть больше, чем полностью. В добавок к этому иерархии маршрутов придется строить в ручную. Например:
            Есть страница каталога и подкаталога. Обе страницы должны открываться в браузере. И получаем, что пользователь для первой страницы должен дать маршрут и для второй страницы тоже дать маршрут и потом искать по одному параметру данные в базе. Или же дать один маршрут на две страницы, но с двумя параметрами и искать в базе по двум параметрам. Адина:( А если бы какой-то поиск побыстрее был в MVC, то пользователь написал Friendly name и все, забыл об этом, а ты просто маршруты переписал вида a\b\c\d\...\n.

            По поводу поиска я в курсе как он происходит. От этого жить не легче, учитывая, что многие не понимают, что роутинг работает в обе стороны. И всевозможные Url.Action тоже используют роутинг. Учитывая, что на странице могут быть десятки ссылок (и бывают в 99.9% случаев), то тормозить ваше творение будет безбожно. Но, хочу сказать, что лично я не знаю, как быстро ускорить поиск, но если бы у меня была возможность вмешаться в этот поиск, я бы его построил через хэши строк, ведь я знаю, что у меня параметров нет и достаточно полного совпадения строк.


            1. masterL
              02.10.2015 11:38

              но если бы у меня была возможность вмешаться в этот поиск, я бы его построил через хэши строк, ведь я знаю, что у меня параметров нет и достаточно полного совпадения строк.

              А почему тогда не рассмотрите вариант с регистрацией маршрутов на основе своей реализации RouteBase, которая будет осуществлять такой поиск?


              1. vladimirkolyada
                02.10.2015 11:41

                Что мне даст RouteBase, если поиск осуществляется перебором тех самых RouteBase? Мне не перебор нужен, линейный, а хотя бы Dictionary подобная структура.


                1. masterL
                  02.10.2015 11:57

                  Да, верно. Ну во всяком случае в ASP.NET 5 есть возможность использовать свою реализацию RouteCollection.


            1. masterL
              02.10.2015 19:20

              Подумал еще над вашим случаем. Если от системы маршрутизации все что требуется — это определить id по полному совпадению строки маршрута, то может быть свести ее использование к минимуму? Создать единственный маршрут и задать ему только «жадный» сегмент в качестве шаблона, чтобы он «съел» весь адрес. А маршрут введенный пользователем, хэш маршрута и id страницы, хранить где-то в приложении в удобной структуре. Ну и собственно, как в предыдущем моем варианте — перед использованием id в приложении заполнять его в RouteData, а искать создавая хэш из строки полученного запроса и ищя его в этой структуре — как вы и хотите.


              1. vladimirkolyada
                02.10.2015 19:46

                Да, я в курсе об этом варианте, его самым первым предлагают, но опять же, хочется все таки и параметры оставить. И рыбку съесть и гвоздь не трогать.