Saturday, October 11, 2014

Refactoring peace of mind with ASP.NET MVC

If the ASP.NET MVC view has this code:
// http://stackoverflow.com/questions/6852979/get-current-controller-in-view

string controller = (string)this.ViewContext.RouteData.Values["controller"];
string action = (string)this.ViewContext.RouteData.Values["action"]; // can use this too: (string)this.ViewContext.Controller.ValueProvider.GetValue("action").RawValue;

if (controller == "Companies" && action == "Search") 
{
    ...
}


We can improve it by making it refactoring-friendly:
Type controllerType = ViewContext.Controller.GetType();
string action = (string)this.ViewContext.RouteData.Values["action"]; // can use this too: (string)this.ViewContext.Controller.ValueProvider.GetValue("action").RawValue;

if (controllerType == typeof(Erp.Controllers.CompaniesController) 
    && action == StaticReflection.GetMemberName<Erp.Controllers.CompaniesController>(m => m.Search(null)) 
{
    ...
}


The refactoring enabler:
namespace Erp.Helper
{
    // http://joelabrahamsson.com/getting-property-and-method-names-using-static-reflection-in-c/
    public class StaticReflection
    {
        public static string GetMemberName<T>(System.Linq.Expressions.Expression<Func<T, object>> expression)
        {
            if (expression == null)
            {
                throw new ArgumentException(
                    "The expression cannot be null.");
            }

            return GetMemberName(expression.Body);
        }

        public static string GetMemberName<T>(System.Linq.Expressions.Expression<Action<T>> expression)
        {
            if (expression == null)
            {
                throw new ArgumentException(
                    "The expression cannot be null.");
            }

            return GetMemberName(expression.Body);
        }

        private static string GetMemberName(System.Linq.Expressions.Expression expression)
        {
            if (expression == null)
            {
                throw new ArgumentException(
                    "The expression cannot be null.");
            }

            if (expression is System.Linq.Expressions.MemberExpression)
            {
                // Reference type property or field
                var memberExpression =
                    (System.Linq.Expressions.MemberExpression)expression;
                return memberExpression.Member.Name;
            }

            if (expression is System.Linq.Expressions.MethodCallExpression)
            {
                // Reference type method
                var methodCallExpression = (System.Linq.Expressions.MethodCallExpression)expression;
                return methodCallExpression.Method.Name;
            }

            if (expression is System.Linq.Expressions.UnaryExpression)
            {
                // Property, field of method returning value type
                var unaryExpression = (System.Linq.Expressions.UnaryExpression)expression;
                return GetMemberName(unaryExpression);
            }

            throw new ArgumentException("Invalid expression");
        }

    }
}



No approach is complete if it is not wrapped in a fluent API. Now your code is completely free of string, typos could be avoided:
if (ViewContext.Controller.Verify<Erp.Controllers.CompaniesController>().IsTheContext())
{
    ...
}


If we want to detect both controller and action:
if (ViewContext.Controller
    .Verify<Erp.Controllers.CompaniesController>().WithAction(m => m.Search(null)).IsTheContext())
{
    ...
}





The supporting API:
public static class StaticReflectionExtension
{
    public static ControllerDetector<T> Verify<T>(this System.Web.Mvc.ControllerBase controller) 
        where T : System.Web.Mvc.ControllerBase
    {
        return new ControllerDetector<T>(controller);
    }
}

public class ControllerDetector<T> where T : System.Web.Mvc.ControllerBase
{
    System.Web.Mvc.ControllerBase _controller;
    string _action = "";
    public ControllerDetector(System.Web.Mvc.ControllerBase controller) 
    {
        _controller = controller;
    }

    public bool IsTheContext()
    {
        return 
            _controller.GetType() == typeof(T)
            && 
            (
                _action == ""

                ||

                _action == (string)_controller.ValueProvider.GetValue("action").RawValue
            );
    }

    public ControllerDetector<T> WithAction(System.Linq.Expressions.Expression<Func<T, object>> expression)
    {            
        _action = StaticReflection.GetMemberName<T>(expression);
        return this;
    }
}




Happy Coding!

No comments:

Post a Comment