2014-02-21 4 views
4

В простом приложении 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: это не настоящий код, просто что-то пытается. Разве это то, что я определенно не должен делать? Зачем?

ответ

6

я в конечном итоге проверки, если текущий запрос имеет предыдущий обработчик. Похоже, что он устанавливается только на детские действия.

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

(HttpContext.Current.PreviousHandler as MvcHandler)! = Null –

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

UPDATE: После вы сказали Factory не работает, единственный способ, которым я мог бы понять, что вы имеете доступ к 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>();

+0

Благодарим за ответ. Я действительно думал о заводском подходе.Но мне нужно выяснить, является ли это дочерним запросом во время или до создания контроллера (например, например, на фабрике пользовательского контроллера), поэтому передача фабрики не работала. Я попытался упростить этот вопрос, приношу извинения, если это вызвало путаницу. – labroo

+0

Вам это понадобится для конкретных контроллеров или для всех из них? У меня есть решение, но я хотел бы знать, что – cvbarros

+0

Это понадобится для всех. – labroo