Find if the request is a request for actions with children, before the controller context is available

In a simple mvc 4 application, I installed the Ninject.MVC3 nuget package.

This is my controller, very simple, ISomeClass is injected in the constructor by ninject.

public class HomeController : Controller
{
    private readonly ISomeClass _someClass;

    public HomeController(ISomeClass someclass)
    {
        _someClass = someclass;
    }

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

    [ChildActionOnly]
    public PartialViewResult MiniView()
    {
        return PartialView("miniview", _someClass.GetName());
    }
}

This is SomeClass

public class SomeClass : ISomeClass
{
    private readonly string _someName;

    public SomeClass(string someName)
    {
        _someName = someName;
    }

    public string GetName()
    {
        return _someName;
    }
}

In the view of Index.cshtml I have

@{ Html.RenderAction("MiniView","Home"); }

Now in NinjectWebCommon, when I go to register the service, I need to know if the request was a requested child action or not. For example, when I call Html.RenderAction. This is what I am trying, but it does not work.

kernel.Bind<ISomeClass>().To<SomeClass>()
      .WithConstructorArgument("someName", c => IsChildAction(c) ? "Child" : "Nope");

IsChildAction method - always returns false.

private static bool IsChildAction(Ninject.Activation.IContext c)
{
   var handler = HttpContext.Current.Handler;

   /*Cant do this, ChildActionMvcHandler is internal*/        
   return handler is System.Web.Mvc.Html.ChildActionExtensions.ChildActionMvcHandler;

//OR

   //This is how ControllerContext.IsChildAction gets its value in System.Web.Mvc but      
   //RouteData.DataTokens is empty for me       
   return ((MvcHandler)handler).RequestContext.RouteData.DataTokens
                              .ContainsKey("ParentActionViewContext");
}  

Any ideas if this can be done?

ps: this is not actual code, just tried something. Is this something that I definitely should not do? Why?

+4
2

, . , .

HttpContext.Current.PreviousHandler != null && 
HttpContext.Current.PreviousHandler is MvcHandler;
+6

IsChildAction - , , SomeClass. factory , , factory. , .

factory Ninject.Extensions.Factory :

public class SomeClassFactory
{
    private readonly IKernel _kernel;

    public SomeClassFactory(IKernel kernel)
    {
        _kernel = kernel;
    }

    public SomeClass Create(string name, bool isChild)
    {
        var childString = (isChild) ? "Child" : "Nope";
        return _kernel.Get<SomeClass>(new ConstructorArgument("someName", childString));
    }
}

UPDATE: , , factory , , RequestContext , factory. , .

SomeClass , RequestContext . IControllerFactory, , , , , .

internal class CustomControllerFactory : DefaultControllerFactory
{
    internal const string ParentActionViewContextToken = "ParentActionViewContext";
    private readonly IResolutionRoot _resolutionRoot;

    public CustomControllerFactory(IResolutionRoot resolutionRoot)
    {
        _resolutionRoot = resolutionRoot;
    }

    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        //You can improve this later if you want -> you'll need to figure out if your controller will fit into this case
        //You can use marker interfaces, common supertype, etc... that up to you
        if (controllerName.Equals("home", StringComparison.InvariantCultureIgnoreCase))
        {
            var controllerType = typeof (HomeController);
            var isChild = requestContext.RouteData.DataTokens.ContainsKey(ParentActionViewContextToken);

            var constructorArgument = new ConstructorArgument("someName", (isChild) ? "Child" : "Nope");
            var requestForDependency = _resolutionRoot.CreateRequest(typeof(IServiceClient), null, new Parameter[] { constructorArgument }, true, true);
            var dependency = _resolutionRoot.Resolve(requestForDependency).SingleOrDefault();

            return (IController)_resolutionRoot.Get(controllerType, new ConstructorArgument("service", dependency));
        }

        //Will go through the default pipeline (IDependencyResolver will be called, not affecting DI of other controllers)
        return base.CreateController(requestContext, controllerName);
    }
} 

, :

kernel.Bind<IControllerFactory>().To<CustomControllerFactory>();

+1

Source: https://habr.com/ru/post/1528057/


All Articles