1

My requirement is to be able to accept the following combinations

{Part1}/{Controller}/{Action}

{Part1}/{Part2}/{Controller}/{Action}

{Part1}/{Part2}/{Part3}/{Controller}/{Action}

and pass them into controllers and methods that will convert Parts 1 to 3 into an ID that is used all over the system. The scheme is:

string id = part1 + "-" + part2 + "-" + part3

The routes are organized in that way to give the appearance of a folder structure, and the controllers / actions are what's available for those "folders".

I'd like to come up with a way of doing this adhering to DRY.

I'm thinking perhaps this is an Action Filter (which I will apply universally) that creates a new entry ID into the RouteValueDictionary, taking the value from the presence of Parts 1 to 3.

Is this the right approach or is there a better solution?

Thank you.

1 Answer 1

1

Here's an idea.

Create some routes

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Special",
        url: "{part1}/{controller}/{action}"
        );

    routes.MapRoute(
        name: "Special2",
        url: "{part1}/{part2}/{controller}/{action}"
        );

    routes.MapRoute(
        name: "Special3",
        url: "{part1}/{part2}/{part3}/{controller}/{action}"
        );

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}",
        defaults: new { controller = "Home", action = "Index" }
        );
}

Example model

[ModelBinder(typeof(SpecialModelBinder))]
public class SpecialModel
{
    public string id { get; set; }
}

Model Binder

public class SpecialModelBinder : DefaultModelBinder
{
    protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
    {
        SpecialModel model = (SpecialModel)bindingContext.Model;
        model.id = string.Join("-", controllerContext.RouteData.Values
            .Where(x => new string[] { "part1", "part2", "part3" }.Contains(x.Key))
            .Select(x => x.Value)
            );
        bindingContext.ModelMetadata.Model = model;
    }
}

Example Controller

public class ManageController : Controller
{
    public ActionResult Edit(SpecialModel model)
    {
        return View();
    }
}

Samples:

http://www.example.com/style1/Manage/Edit

@Model.Id == "style1"

http://www.example.com/style1/red/Manage/Edit

@Model.Id == "style1-red"

http://www.example.com/style1/red/16/Manage/Edit

@Model.Id == "style1-red-16"
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. I agree a ModelBinder is a better place to put this logic. The only drawback I see is that if I use id in multiple classes (as in the case of it being a CategoryId or FolderId), they will all have to inherit from SpecialModel. Right? Is there a way to code this in such a way that it works for ANY class that has field "id"?
You can change the default model binder to your own and then alter the code so that it checks the model for an "id" field and then tries its best to populate it. You can change the default model binder with ModelBinders.Binders.DefaultBinder = new SpecialModelBinder();; put it in your Startup.cs or Global.asax.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.