Policy Injection Block: call handler won't fire

Topics: Policy Injection Application Block
Apr 12, 2012 at 3:21 PM

I'm trying to add logging through AOP in an ASP.NET MVC application using:

  • Enterprise Library Policy Injection application Block
  • StructureMap
  • log4net

This is what I have:

web.config

  <configSections> 
    <section name="policyInjection" type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.Configuration.PolicyInjectionSettings, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" /> 
  </configSections>    
 
  <policyInjection> 
    <policies> 
      <add name="Policy"> 
        <matchingRules> 
          <add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.TagAttributeMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
            match="Log" name="Tag Attribute Matching Rule" /> 
        </matchingRules> 
        <handlers> 
          <add type="MyApp.Website.MyLoggingCallHandler, MyApp.Website, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 
            name="Custom Handler" /> 
        </handlers> 
      </add> 
    </policies> 
  </policyInjection> 

Bootstrapper:

ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory()); 

StructureMapControllerFactory:

public class StructureMapControllerFactory : DefaultControllerFactory 
{ 
  protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) 
  { 
    var instance = ObjectFactory.GetInstance(controllerType); 
    return PolicyInjection.Wrap<IController>(instance); 
  } 
} 

Controller:

[Tag("Log")] 
public class HomeController : Controller 
{ 
  //Implementation 
} 

MyLoggingCallHandler:

namespace MyApp.Website 
{ 
    [ConfigurationElementType(typeof(CustomCallHandlerData))] 
    public class MyLoggingCallHandler : ICallHandler 
    { 
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) 
        { 
          //Implementation 
        } 
 
        public int Order { get; set; } 
    } 
} 

The issue is: the call handler is never fired.
What I am doing wrong?

Apr 13, 2012 at 4:09 AM
Edited Apr 13, 2012 at 9:59 PM

The issue is that the Tag is applied to the HomeController but the requested interface is not actually implemented in HomeController.  It is actually implemented in ControllerBase:

        #region IController Members
        void IController.Execute(RequestContext requestContext) {
            Execute(requestContext);
        }

Policy Injection in Enterprise Library is handled by Unity.  Unity finds the interface implementation on the base class but only checks the base class for the Tag attribute (and not the HomeController instance) so the call handler is not wired up.  I'm not sure if that is a bug or by design.  If you add the Tag to the base class (ControllerBase) then it will work fine.  Unfortunately, ControllerBase is in the MVC source so that is probably not an option.  Another approach might be to change to a different matching scheme via configuration but I haven't had a chance to verify that.

The easiest way to intercept on the Execute method of IController (which is what I think you want to do since you are intercepting IController) is to re-implement the IController interface in the HomeController:

    [Tag("Log")]
    public class HomeController : Controller, IController
    {  
        public ActionResult Index()
        {
            ViewBag.Message = "Welcome to ASP.NET MVC!";

            return View();
        }

        public ActionResult About()
        {
            return View();
        }

        void IController.Execute(System.Web.Routing.RequestContext requestContext)
        {
            base.Execute(requestContext);
        }
    }

Another option might be to not use PolicyInjection and go with Unity interception instead.  That would give a bit more flexibility in what can be intercepted.  E.g. you could use VirtualMethodInterceptor instead of the TransparentProxyInterceptor used for Policy Injection.  Then you could intercept virtual methods directly on your controller.  Since you'd already be (and actually already are!) using Unity for interception, you could also consider using Unity instead of StructureMap for all of the DI/IoC in your app.  :)  

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Apr 14, 2012 at 2:21 PM

This is what I found out after trying out the proposed solution:

  • The call handler is only fired for the "Execute" method, being the only one direcly in IController.
    It there a way to intercept all the ActionResult methods in HomeController?
  • While trying out "custom attribute matching", it only seemed to work on method level, and not on class level.
    Is that correct?

Thank you for your reply.

Apr 15, 2012 at 2:08 AM
Edited Apr 15, 2012 at 9:49 PM

Yes, the call handler is only fired for the Execute method.  Interception is occurring for the IController interface and the Policy Injection interceptor (TransparentProxyInterceptor).  The only method on the IController interface is Execute so that is the method that is intercepted.  And it's only intercepted in the class with the tag applied which also has IController implemented.

Yes, the custom attribute matching is on method/property level.

As I mentioned, you could change from Policy Injection to Unity Interception which is actually the recommended approach for new development (Policy Injection is provided for backward compatibility purposes although you are free to use it if you wish).

You can use Unity Interception along with a Unity IControllerFactory:

    public class UnityControllerFactory : IControllerFactory
    {
        private IUnityContainer _container;
        private IControllerFactory _innerFactory;

        public UnityControllerFactory(IUnityContainer container)
            : this(container, new DefaultControllerFactory())
        {
        }

        protected UnityControllerFactory(IUnityContainer container, IControllerFactory innerFactory)
        {
            _container = container;
            _innerFactory = innerFactory;
        }

        public IController CreateController(RequestContext requestContext, string controllerName)
        {
            try
            {
                return _container.Resolve(controllerName);
            }
            catch (Exception)
            {
                return _innerFactory.CreateController(requestContext, controllerName);
            }
        }

        public void ReleaseController(IController controller)
        {
            _container.Teardown(controller);
        }

        public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
        {
            return System.Web.SessionState.SessionStateBehavior.Default;
        }
    }

    [ConfigurationElementType(typeof(CustomCallHandlerData))]
    public class MyLoggingCallHandler : ICallHandler
    {
        public MyLoggingCallHandler(NameValueCollection nvc)
        {
        }

        public MyLoggingCallHandler()
        {
        }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            return getNext()(input, getNext);
        }

        public int Order { get; set; }
    }

Then in global.asax you can add the configuration:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    IUnityContainer container = new UnityContainer();

    container
        .AddNewExtension<Interception>()
        .RegisterType<MyLoggingCallHandler>()
        // Register the type to be intercepted
        .RegisterType<IController, HomeController>("Home",
                new Interceptor<VirtualMethodInterceptor>(),
                new InterceptionBehavior<PolicyInjectionBehavior>())
        // Configure policies
        .Configure<Interception>()
            .AddPolicy("policy1")
                .AddCallHandler(new MyLoggingCallHandler(null))
                .AddMatchingRule(new ReturnTypeMatchingRule(typeof(ActionResult)));

    ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container)); 
}

This configures interception on any method that returns an ActionResult.  Of course, you can restrict by other matching rules (e.g. namespace, type, parameters ,etc.).  If you wanted to stick with an attribute you could still do that using ".AddMatchingRule(new TagAttributeMatchingRule("Log"));"

The HomeController would look like:

    public class HomeController : Controller
    {  
        public virtual ActionResult Index(RequestContext context)
        {
            ViewBag.Message = "Welcome to ASP.NET MVC!";

            return View();
        }

        public virtual ActionResult About()
        {
            return View();
        }
    }

Notice that the methods are virtual to support the VirtualMethodInterceptor.

See  ASP.NET MVC 3 Dependency Injection for a discussion and exercise.

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Apr 16, 2012 at 10:00 AM
Edited Apr 16, 2012 at 12:16 PM

Thank you for the solution.
I'll definitely use it in future projects.
I'm afraid the currect one is too far out to replace the DI container.

I was thinking of the following solution:

public interface IHomeController : IController
{
    ActionResult Overview();
}
public class HomeController : IHomeController
{
    [Tag("Log")]
    public ActionResult Overview()
    {
        //Implementation
    }
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
    var instance = ObjectFactory.GetInstance(controllerType);
//get IHomeController Type ifaceType = instance.GetType() .GetInterfaces() .FirstOrDefault(i => i.GetInterfaces()
.Any(s => s == typeof(IController))) ?? typeof(IController); return PolicyInjection.Wrap(ifaceType, instance) as IController; }

Two questions about that:

  • Is there something inherently evil about what I'm trying to do?
  • Why doesn't the interception work? :P
Apr 17, 2012 at 5:04 AM

I don't think that approach helps much.  It's a good try ( :) ) but you are back to the issue that the runtime is treating the type as an IController (it's what GetControllerInstance returns).  The only method on IController is Execute so that is the only method that is eligible for interception because only interface methods are eligible for interception (unless the object inherits from MarshalByRefObject).  However, the Execute method, defined in a base class, is not decorated with the Tag("Log") attribute.  The interception behavior is a limitation of PolicyInjection.

Using the current approach I think the best you can do is intercept on the ControllerBase Execute method.  This is what invokes the controller's action so it's fairly close to the calls you are interested in.  I would use configuration to set a matching rule to ControllerBase:

  <policyInjection> 
    <policies> 
      <add name="Policy"> 
        <matchingRules>
          <add type="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.MatchingRules.TypeMatchingRule, Microsoft.Practices.EnterpriseLibrary.PolicyInjection, Version=5.0.505.0, Culture=neutral, PublicKeyToken=null"
              name="Type Matching Rule">
            <matches>
              <add match="System.Web.Mvc.ControllerBase" ignoreCase="true" />
            </matches>
          </add>
        </matchingRules>
        <handlers> 
          <add type="MyApp.Website.MyLoggingCallHandler, MyApp.Website, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 
            name="Custom Handler" /> 
        </handlers> 
      </add> 
    </policies> 
  </policyInjection>

If that doesn't meet your needs, then perhaps StructureMap interception can help for your current project?

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com