在一个简单的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:这不是真正的代码,只是尝试一些东西。这是我应该肯定不会做的事情吗?为什么呢?
答案 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>();