Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace a controller action at runtime

We have a deployed ASP.NET MVC 3 application, and have discovered a bug in one of the controller actions. For political reasons, we are not allowed to replace any existing code without a big process that takes weeks of headache. I.e. we can't rebuild the MVC application.

We are allowed to deploy a new assembly, containing a new controller with just the one fixed action.

Is there a way to add to or edit the routes of the MVC application to map the new controller action?


I am considering subclassing my MVC application in the hotfix DLL and updating global.asax to reference the subclassed application.

public class HotfixApplication : RealApplication
{
    public override void Init()
    {
        base.Init();
        var badRoute =  RouteTable.Routes.Where(...);
        var badRoute.FixIt();
    }
}

And it would route to a controller action in the hotfix DLL.

Does this sound plausible? Safe?

like image 334
Craig Celeste Avatar asked May 04 '12 17:05

Craig Celeste


1 Answers

Try this approach based on ControllerFactory, HttpModule:

Steps:

1# Create a new 'Class Library' Project. Add reference to your asp.net mvc web application project. Also add reference to 'System.Web', 'System.Web.Mvc', 'Microsoft.Web.Infrastructure.dll' assemblies.

2# Create a new Controller class inherited from the controller (TargetController) that has the bug:

public class FixedContorller : TargetController
{

    [FixedActionSelector]
    [ActionName("Index")]
    public ActionResult FixedIndex()
    {
        ViewBag.Title = "I'm Fixed...";
        return View();
    }
}

The [FixedActionSelectorAttribute] is ActionSelector attribute, used to resolve the action name.

    public class FixedActionSelector : ActionMethodSelectorAttribute
    {
    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        return true;
    }
}

3# Define a custom ControllerFactory, that will create the fixed controller instead of the target controller:

public class MyControllerFactory : DefaultControllerFactory
{
    private static string targetControllerName = "Target";
    private static string targetActionName = "Index";

    protected override Type GetControllerType(System.Web.Routing.RequestContext requestContext, string controllerName)
    {
        var action = requestContext.RouteData.Values["action"].ToString();

        if (targetControllerName.Equals(controllerName, StringComparison.InvariantCultureIgnoreCase) &&
            targetActionName.Equals(action, StringComparison.InvariantCultureIgnoreCase))
        {
            return typeof(FixedContorller);
        }

        return base.GetControllerType(requestContext, controllerName);
    }
}

4# Now define a HttpModule, that will set the above controller-factory on application init. The httpModule will register itself programmatically, no need to register in web.config.

using System;
using System.Web;
using System.Web.Mvc;

[assembly: PreApplicationStartMethod(typeof(YourApp.Patch.FixerModule), "Register")]
namespace YourApp.Patch
{
    public class FixerModule : IHttpModule
    {
        private static bool isStarted = false;
        private static object locker = new object();

        public void Dispose()
        {
        }

        public void Init(HttpApplication context)
        {
            if (!isStarted)
            {
                lock (locker)
                {
                    if (!isStarted)
                    {
                        ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));
                        isStarted = true;
                    }
                }
            }
        }

        public static void Register()
        {
            Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(FixerModule));
        }
    }
}

Compile the project and copy the .dll in the bin folder of your web application.

Hopes this helps...

like image 147
Kibria Avatar answered Oct 17 '22 17:10

Kibria