Castle Windsor - Injecting IActionInvoker Implementation Issue

回眸只為那壹抹淺笑 提交于 2019-12-29 05:34:13

问题


I am trying to use the approach from this article, but I am missing something - I am currently getting an error within WindsorControllerFactory.GetControllerInstance when it tries to resolve IActionInvoker since WindsorActionInvoker has a dependency on IWindsorContainer.

Given that WindsorControllerFactory already has a reference to IWindsorContainer, could I pass that reference in? If so - how? The only examples I have found are about passing value types as constructor parameters, not reference types.

I'm feeling I'm missing something obvious...

Current setup as follows: Within Global.asax Application_Start I call the following method:

 protected virtual IWindsorContainer InitializeServiceLocator()
    {
        IWindsorContainer container = new WindsorContainer();
        ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));

        container.RegisterControllers(typeof(HomeController).Assembly);
        ComponentRegistrar.AddComponentsTo(container);

        return container;
    }

ComponentRegistrar:

 public static void AddComponentsTo(IWindsorContainer container) 
 {
      //add other components.....

      container.AddComponentLifeStyle<IActionInvoker, WindsorActionInvoker>(LifestyleType.PerWebRequest);

 }

WindsorActionInvoker:

public class WindsorActionInvoker : ControllerActionInvoker, IActionInvoker
{
    readonly IWindsorContainer container;

    public WindsorActionInvoker(IWindsorContainer container)
    {
        this.container = container;
    }

    protected override ActionExecutedContext InvokeActionMethodWithFilters(
            ControllerContext controllerContext,
            IList<IActionFilter> filters,
            ActionDescriptor actionDescriptor,
            IDictionary<string, object> parameters)
    {
        foreach (IActionFilter actionFilter in filters)
        {
            container.Kernel.InjectProperties(actionFilter);
        }
        return base.InvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters);
    }
}

WindsorControllerFactory:

 public class WindsorControllerFactory : DefaultControllerFactory
{
    private readonly IWindsorContainer container;

    public WindsorControllerFactory(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        this.container = container;
    }

    public override void ReleaseController(IController controller)
    {
        var disposable = controller as IDisposable;

        if (disposable != null)
        {
            disposable.Dispose();
        }

        this.container.Release(controller);
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            return base.GetControllerInstance(requestContext, controllerType);
        }

        var controller = this.container.Resolve(controllerType) as Controller;

        if (controller != null)
        {
            controller.ActionInvoker = this.container.Resolve<IActionInvoker>(this.container);
        }

        return controller;
    }
}

Update

I missed a subtlety: I was trying to use this behaviour for the following:

public class CustomAuthorize : AuthorizeAttribute {...}

which doesn't implement IActionFilter.

Added the following to WindsorActionInvoker:

 protected override AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
    {
        foreach (IAuthorizationFilter authorizeFilter in filters)
        {
            this.kernel.InjectProperties(authorizeFilter);
        }
        return base.InvokeAuthorizationFilters(controllerContext, filters, actionDescriptor);
    }

This now works as required. Thanks to Cristiano since it was analysis of his kindly provided code which put me on the right path.


回答1:


Global.asax

private static void bootstrapContainer()
{
    container = new WindsorContainer()
        .Install(FromAssembly.This());
    var controllerFactory = new WindsorControllerFactory(container.Kernel);

    ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}

Installer / filling container

public class ControllersInstaller : IWindsorInstaller
{
    #region IWindsorInstaller Members

    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Component.For<WpRegistration.Web.Filters.AgencyAuthorize>().LifeStyle.Transient);
        container.Register(Component.For<IActionInvoker>().ImplementedBy<WindsorExtensions.Mvc.WindsorActionInvoker>().LifeStyle.Transient);

        container.Register(AllTypes.FromThisAssembly()
                            .BasedOn<IController>()
                            .If(Component.IsInSameNamespaceAs<HomeController>())
                            .If(t => t.Name.EndsWith("Controller"))
                            .Configure((c => c.LifeStyle.Transient)));
    }

    #endregion
}

WindsorControllerFactory

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

using Castle.MicroKernel;

public class WindsorControllerFactory : DefaultControllerFactory
{
    private readonly IKernel kernel;

    public WindsorControllerFactory(IKernel kernel)
    {
        this.kernel = kernel;
    }

    public override void ReleaseController(IController controller)
    {
        kernel.ReleaseComponent(controller);
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
        }

    IController iController = (IController)kernel.Resolve(controllerType);

    // new code    
    if (iController is Controller)    
    {
        ((Controller)iController).ActionInvoker = kernel.Resolve<IActionInvoker>();
    }

    return iController;
    }
}

WindsorActionInvoker

namespace WindsorExtensions.Mvc
{

    public class WindsorActionInvoker : ControllerActionInvoker
    {
        readonly IKernel kernel;
        public WindsorActionInvoker(IKernel kernel) 
        { 
            this.kernel = kernel; 
        }
        protected override ActionExecutedContext InvokeActionMethodWithFilters(
            ControllerContext controllerContext
            , IList<IActionFilter> filters
            , ActionDescriptor actionDescriptor
            , IDictionary<string, object> parameters)
        {
            foreach (IActionFilter actionFilter in filters)
            {
                kernel.InjectProperties(actionFilter);
            }
            return base.InvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters);
        }
    }

    public static class WindsorExtension 
    { 
        public static void InjectProperties(this IKernel kernel, object target) 
        { 
            var type = target.GetType(); 
            foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) 
            { 
                if (property.CanWrite && kernel.HasComponent(property.PropertyType)) 
                { 
                    var value = kernel.Resolve(property.PropertyType); 
                    try { property.SetValue(target, value, null); } 
                    catch (Exception ex) 
                    { 
                        var message = string.Format("Error setting property {0} on type {1}, See inner exception for more information.", property.Name, type.FullName);
                        throw new ComponentActivatorException(message, ex); 
                    }
                }
            }
        }
    }
}

AgencyAuthorizeAttribute

namespace WpRegistration.Web.Filters 
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public sealed class AgencyAuthorize : ActionFilterAttribute
    {
        CurrentUserService _currentUserSvc;
        public AgencyAuthorize() { }

        public CurrentUserService Service
        {
            get { return _currentUserSvc; }
            set
            {
                this._currentUserSvc=value;
            }
        }


来源:https://stackoverflow.com/questions/6622613/castle-windsor-injecting-iactioninvoker-implementation-issue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!