在控制器上下文可用之前查找请求是否是子操作请求

时间:2014-02-21 08:52:43

标签: c# asp.net-mvc asp.net-mvc-4 ninject

在一个简单的mvc 4应用程序中,我安装了Ninject.MVC3 nuget包。

这是我的控制器,非常基本,ISomeClass由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());
    }
}

这是SomeClass

public class SomeClass : ISomeClass
{
    private readonly string _someName;

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

    public string GetName()
    {
        return _someName;
    }
}

在Index.cshtml视图中我有

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

现在在NinjectWebCommon中,当我去注册服务时,我需要知道请求是否是子动作请求。就像我打电话给Html.RenderAction一样。这就是我正在尝试但它无法正常工作。

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

IsChildAction方法 - 始终返回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");
}  

如果可以做到这一点有什么想法吗?

ps:这不是真正的代码,只是尝试一些东西。这是我应该肯定不会做的事情吗?为什么呢?

2 个答案:

答案 0 :(得分:6)

我最终检查当前请求是否有前一个处理程序。似乎它只是针对儿童行为设置的。

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

答案 1 :(得分:1)

如果IsChildAction仅在运行时和控制器内已知,我建议您不要传递SomeClass的实例。改为通过工厂,并在需要时使用工厂构建该实例。我认为这种方法在你的情况下效果最好。

使用工厂,您可以使用 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));
    }
}

<强>更新 在您说工厂不起作用之后,在控制器创建期间,我唯一能够让您访问RequestContext的方法是使用自定义控制器工厂。我担心你无法在你的绑定中静态得到它。

以下代码在运行时对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's 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>();