Skip to content

Improvements to DynamicRouteValueTransformer #21471

Closed
@rynowak

Description

@rynowak

Summary

3.1 Introduced DynamicRouteValueTransformer - a feature that allows you to use a custom endpoint to dynamically select a controller-action or a razor page.

This is useful when you want to create a slug route that accesses the database to choose where to go, or when you're layering another framework on top of MVC (OData).

Now that we've had a chance to get some feedback on it's clear that we're missing a few features that ought to be there.

Context

The way that DynamicRouteValueTransformer is hooked up, there's no way to pass additional context to it. DynamicRouteValueTransformer comes from DI.

endpoints.MapDynamicControllerRoute<MyTransformer>("...");

If you have multiple endpoints that use the same transformer type, you can't pass any data to them, because they come from DI. You effectively need a distinct type for each instance, which isn't a solution. Both OData and Orchard hit this issue.

Filtering

You might also want to do some filtering based on the results of your transformer. MVC resolves the matching endpoints based on the set of route values returned by a transformer, but the transformer can't do any filtering, or see the endpoints that resulted.

See: https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/Routing/DynamicControllerEndpointMatcherPolicy.cs#L121

Writing a separate policy to handle this doesn't work well. The transformer has context that is lost when you get past this phase:

  • A transformer could filter just the endpoints it produced.
  • A transformer could store context across method calls if it is transient

A policy that runs later can't see which endpoints came from the transformer, and can't store context because policies are singletons.

Ordering

The methods we provide for registering transformers don't support providing an Order. That's a fairly important thing when considering that transformers will be interleaved with other MVC endpoints.

However MapDynamicControllerRoute doesn't follow the same ordering rules as MapControllerRoute - it always uses order 0.

We should consider a breaking change to make MapDynamicControllerRoute follow the ordering rules of MapControllerRoute and also allow fine-grained control over the order value.

Fixes

We should add some functionality like the following:

public abstract class DynamicRouteValueTransformer
{
        // Will be set by the framework when initialized. This needs to come with the guidance
        // to use Transient DI lifetime.
        public object State { get; set; }

        public abstract ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values);

        // Will be called when a non-zero amount of endpoints is found by MVC after calling
        // TransformAsync. They must return a new list of endpoints to perform filtering, or can
        // return the original list to skip.
        // The route values will be the ones returned from TransformAsync
        public virtual ValueTask<IReadOnlyList<Endpoint>> FilterEndpointsAsync(HttpContext httpContext, IReadOnlyList<Endpoint> endpoints, RouteValueDictionary values);
}

And the ability to pass in state:

endpoints.MapDynamicControllerRoute<MyTransformer>("...", state: new MyObject());

Metadata

Metadata

Assignees

Labels

DoneThis issue has been fixedarea-mvcIncludes: MVC, Actions and Controllers, Localization, CORS, most templatesenhancementThis issue represents an ask for new feature or an enhancement to an existing onefeature-routing

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions