简体   繁体   中英

How to Refactor ASP.Net MVC ActionResult?

As I get further into building my ASP.Net MVC website, I am encountering some hard-coding that I want to refactor into something a little be more flexible.

In my views, I have been passing a MenuItemModel that looks like this:

public class MenuItemModel {
    public MenuItemModel(string aLinkName, string aArea
                              , string aController, string aAction) {

    public string LinkName { get; private set; }
    public string Area { get; private set; }
    public string Controller { get; private set; }
    public string Action { get; private set; }

    public bool isActive { get; set; }
}

So that in the view, I can create a link with this:

@Html.ActionLink( @Model.LinkName , @Model.Action 
            , @Model.Controller , new { Area = @Model.Area } , new { } )

This means that I have to create a bunch of MenuItemModels like this:

MenuItemModel MIM=new MenuItemModel("History","Transactions","History","Index");

This works fine, it creates the right links.

But I see a big problem looming in the future .. this strategy isn't resilient to refactoring abilities in Resharper. I want the luxury of refectoring my Area Names, Controller Names and even the Action names in the future as we add new features. By hard coding the literal "Transactions" for the area name, I must now manually refactor the entire website to find all the "Transactions" literals and replace them with something else if we change the area name; or break a area into smaller areas, etc. Yuck.

Is there a way to get the controller name, area and action name from if I were to instantiate a link to the ActionResult? Is there a Helper out there I don't know about?

One way is to use:

myController.GetType()

And inspect the type attributes and parse out the Area and Controller name from the "FullName" in the Type, but I don't have that luxury with the method ... it returns type information for the ActionResult and has no identifying names.

Thoughts?

You could use T4 MVC templates to generate strongly typed helpers to eliminate literals. They certainly help with refactoring although they can be a bit cumbersome to work with (and will add noise to your resharper type searches).

There are two ways I can think of:

  1. Using the nameof keyword assuming you are using C# version 6 or higher.
  2. Using lambda expressions

Using nameof

Going back to your code, I think you can use nameof with something like this:

var controllerName = typeof(HistoryController).Name;
var mvcNameOfController = 
    controllerName.Substring(0, controllerName.LastIndexOf("Controller"));
var actionName = nameof(History.Index);

var menuItemModel = 
    new MenuItemModel("History","Transactions", mvcNameOfController, actionName);

Using lambda expressions

With lambda expressions you can do something like this:

public static MenuItemModel GetMenuItemFor<TController>(
    string aLinkName, string aArea, 
    Expression<Action<TController>> expression
) where T : Controller
{
    var controllerName = typeof(T).Name;
    var mvcNameOfController = 
        controllerName.Substring(0, controllerName.LastIndexOf("Controller"));
    var methodName = ((MethodCallExpression)expression.Body).Method.Name;
    return new MenuItemModel(aLinkName, aArea, mvcNameOfController, methodName);
}

Which you can use like this:

var menuItemModel = 
    GetMenuItemFor<HistoryController>("History", "Transactions", x => x.Index());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM