ASP.NET MVC 3: Use re-mix library to add controller actions with mixins – Part 2

This is an update to my previous post “ASP.NET MVC 3: Use re-mix library to add controller actions with mixins”. In November last year I implemented a prototypical approach that uses mixins to add actions to ASP.NET MVC 3 controllers. Since .NET does not support the mixin concept out of the box, I used the re-mix library in this implementation.

Even though my implementation basically worked, there were several limitations (listed at the end of the blog post) that hinders using this approach in a real world project. Thanks to Fabian Schmied and his blog post “re-mix: Encapsulate and share ASP.NET MVC controller actions” it turned out that I was not aware of all the cool re-mix features that could solve the problems.

In this blog post I will show how to use Fabian’s suggestions to improve my first implementation. Summarized, his suggestions lead to a very a smooth integration of the re-mix library into a ASP.NET MVC 3 web application that enables adding actions to controllers without complex inheritance structures.

To understand the steps described below in detail it could be reasonable to download the source code of my first implementation. At the end of this post you find the download link for the updated version that includes all improvements.

Use IntroducedMemberVisibility = MemberVisibility.Public

When adding the Uses attribute to the controllers one could set IntroducedMemberVisibility = MemberVisibility.Public to tell re-mix to implement interface members publicly. As a result there is no need for the custom ControllerActionInvoker; the default ASP.NET MVC 3 ControllerActionInvoker will now find the actions defined in the mixins as if there were defined directly in the controller (like “normal” actions).
Instead of setting the option every time the remix UsesAttribute is used to annotate a controller, one could (as Fabian suggested) define a custom ControllerUsesMixinAttribute that takes care of setting the option:

public class ControllerUsesMixinAttribute : UsesAttribute
{
  public ControllerUsesMixinAttribute(Type mixinType) : base(mixinType)
  {
    IntroducedMemberVisibility = MemberVisibility.Public;
  }
}

and use this attribute to annotate the controllers:

[ControllerUsesMixinAttribute(typeof(EchoControllerMixin))]
public class Controller1Controller : Controller
{
}

Defined a IMixedController interface

To access the protected method defined in the default ASP.NET MVC 3 ControllerBase class one could specify a custom interface IMixedController like

public interface IMixedController
{
  ContentResult Content(string content);
  ControllerContext ControllerContext { get; }
}

and use this interface as TTarget type when defining the mixin class:

public class EchoControllerMixin : Mixin<IMixedController>, IEchoControllerMixin
{
  public ActionResult Echo(string id)
  {
    var controllerName = Target.ControllerContext.RouteData.Values["controller"].ToString();
    return Target.Content(string.Format("re-mix added 'Echo' action to controller '{0}'<hr/>You said: {1}", controllerName, !string.IsNullOrEmpty(id) ? id : "<i>nothing</i>"));
  }
}

Of course there are many more methods and properties defined in the ControllerBase class that could be added to the IMixedController interface.

RemixControllerFactory: Subclass DefaultControllerFactory instead of implementing IControllerFactory

The custom controller factory RemixControllerFactory is responsible for creating the concrete controller instance (which might be a re-mix proxy object if the controller has any UsesAttributes). In my first implementation I used ASP.NET MVC DefaultControllerFactory to create the controller object (without the “intermixed” actions) to get the type of the controller that should be used. In a second step, this type was passed to re-mix’s ObjectFactory to create the “correct” controller (containing the “intermixed” actions). The drawback of this approach is that the controller object is created twice.

A much better way is to subclass DefaultControllerFactory and override GetControllerType():

public class RemixControllerFactory : DefaultControllerFactory
{
  protected override Type GetControllerType(RequestContext requestContext, string controllerName)
  {
    var baseControllerType = base.GetControllerType(requestContext, controllerName);
    return Remotion.Mixins.TypeFactory.GetConcreteType(baseControllerType);
  }
}

I updated the source code and the Visual Studio 2010 project, you can download the new version here.

Comments

  1. Fabian Schmied

    Thanks for trying out my suggestions, great to read that they actually worked ;)

Leave a comment

Your email address will not be published. Required fields are marked *

*