Async MVC Action with Castle Windsor
Asked Answered
P

2

6

I have an MVC5 application that uses Castle Windsor (http://www.artisancode.co.uk/2014/04/integrating-windsor-castle-mvc/). I recently tried to add an Async method an MVC Controller. When I do this I receive the following error message:

The asynchronous action method 'test' returns a Task, which cannot be executed synchronously.

I created a new MVC application in VS and did not receive the error, so I'm guessing I'v left something out of the Castle Windsor configuration? However I've no idea where to begin and I've been unable to find any article when helps.

Updating question with code:

CastleWindsorActionInvoker.cs

public class CastleWindsorActionInvoker : ControllerActionInvoker
    {
        private readonly IKernel kernel;

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

        protected override ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
        {
            foreach(IActionFilter filter in filters)
            {
                kernel.InjectProperties(null, filter);
            }

            return base.InvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters);
        }

        protected override AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
        {
            foreach(IAuthorizationFilter filter in filters)
            {
                Type type = filter.GetType();

                IEnumerable<INamedInstanceAttribute> namedInstanceAttributes = type.GetCustomAttributes(typeof(INamedInstanceAttribute), false) as IEnumerable<INamedInstanceAttribute>;

                if(namedInstanceAttributes != null)
                {
                    this.kernel.InjectProperties(namedInstanceAttributes, filter);
                }
                else
                {
                    this.kernel.InjectProperties(null, filter);
                }
            }

            return base.InvokeAuthorizationFilters(controllerContext, filters, actionDescriptor);
        }
    }

WindsorDependencyMvcResolver.cs

public class WindsorDependencyMvcResolver : System.Web.Mvc.IDependencyResolver
    {
        public IWindsorContainer container { get; protected set; }

        public WindsorDependencyMvcResolver(IWindsorContainer container)
        {
            if(container == null)
            {
                throw new ArgumentNullException("container");
            }

            this.container = container;
        }

        public object GetService(Type serviceType)
        {
            try
            {
                return container.Resolve(serviceType);
            }
            catch(ComponentNotFoundException)
            {
                return null;
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return container.ResolveAll(serviceType).Cast<object>();
        }
    }

CastleWindsorMvcFactory .cs

public class CastleWindsorMvcFactory : DefaultControllerFactory
    {
        private readonly IKernel kernel;

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

        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));
            }

            Controller controller = (Controller)kernel.Resolve(controllerType);

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

            return controller;
        }

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

Global.asax

ControllerBuilder.Current.SetControllerFactory(new CastleWindsorMvcFactory(container.Kernel));

DependencyResolver.SetResolver(new WindsorDependencyMvcResolver(container));

MVC Action

public async Task<ActionResult> Index()
        {
            return View();
        }
Pazit answered 10/8, 2015 at 1:52 Comment(3)
Start by showing us your codeFusilier
Is this action called by Html.Action method in another view?Caundra
It's just a test method, it called directly by entering the url in a browserPazit
P
2

I was working on something else and came across this article. Based on it, I changed my CastleWindsorActionInvoker to inherit from AsyncControllerActionInvoker and I was able to run my async action.

Thanks for all suggestions!

Pazit answered 11/8, 2015 at 7:5 Comment(0)
B
1

Disclaimer: the links below are for a sample Github project I created.

I would suspect that article is recommending the wrong lifestyle for your controllers. I normally use Transient, like so:

public class ControllersInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly()
            .BasedOn<IController>()
            .LifestyleTransient());

        container.Register(Classes.FromThisAssembly()
            .BasedOn<IHttpController>()
            .LifestyleTransient());
    }
}

Using that installer, this action resolves just fine:

public async Task<ActionResult> Login(Models.LoginFormModel model, string returnUrl = "")
{
    try
    {
        if (ModelState.IsValid)
        {
            /* ... more code ... */
        }
    }
    catch (Exception ex)
    {
        HandleException(ex);
    }

    // If we got this far, something failed; redisplay form
    return View(model);
}
Bedside answered 10/8, 2015 at 4:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.