MVC5 Ninject 绑定和 HttpContext [英] MVC5 Ninject binding and HttpContext

查看:19
本文介绍了MVC5 Ninject 绑定和 HttpContext的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试建立一个新项目,并且我添加了一个新类 MembershipService,该类要求在其构造函数中传递 HttpContext.

I am trying to set up a new project and I've added a new class MembershipService that requires the HttpContext to be passed in it's constructor.

在以前的项目中我使用了代码

In a previous project I used the code

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IMembershipService>()
            .To<MembershipService>()
            .InRequestScope()
            .WithConstructorArgument("context", HttpContext.Current);
       ....
    }

但是在新项目中,我使用的是 Ninject Modules,在 StackOverflow 和 Google 上进行了一些搜索后,我想出了以下代码:公共类 ServiceHandlerModule : NinjectModule{

However in the new project I'm using Ninject Modules, and after some searching on StackOverflow and Google, I've come up with the code below: public class ServiceHandlerModule : NinjectModule {

    public override void Load()
    {

        Bind<IMembershipService>()
            .To<MembershipService>()
            .WithConstructorArgument("context", ninjectContext=> HttpContext.Current);


        this.Kernel.Bind(x =>
        {
            x.FromAssemblyContaining(typeof(NinjectWebCommon))
                .SelectAllClasses()
                .Where(t => t != typeof(MembershipService))
                .BindDefaultInterface();
        });
        this.Kernel.Bind(x =>
        {
            x.FromAssemblyContaining<BrandServiceHandler>()
                .SelectAllClasses()
                .Where(t => t != typeof(MembershipService))
                .BindDefaultInterface();
        });

    }
}

但是,我收到以下错误:

However, I get the error below:

描述:执行过程中发生未处理的异常当前的网络请求.请查看堆栈跟踪以了解更多信息有关错误及其在代码中的来源的信息.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

异常详细信息:Ninject.ActivationException:激活错误string 没有匹配的绑定可用,并且类型不是可自绑定.激活路径:

Exception Details: Ninject.ActivationException: Error activating string No matching bindings are available, and the type is not self-bindable. Activation path:

5) 将依赖字符串注入到参数文件名中HttpRequest 类型的构造函数

5) Injection of dependency string into parameter filename of constructor of type HttpRequest

4) 将依赖HttpRequest注入到参数请求中HttpContext 类型的构造函数

4) Injection of dependency HttpRequest into parameter request of constructor of type HttpContext

3) 将依赖 HttpContext 注入到参数 httpContext 中MembershipService 类型的构造函数

3) Injection of dependency HttpContext into parameter httpContext of constructor of type MembershipService

2) 将依赖 IMembershipService 注入参数HomeController 类型构造函数的membershipService

2) Injection of dependency IMembershipService into parameter membershipService of constructor of type HomeController

1) 请求 HomeController

1) Request for HomeController

有人能指出我哪里出错了吗?

Can someone point out where I'm going wrong?

谢谢,约翰

推荐答案

Steven 关于 HttpContext 作为运行时值的说法是正确的.它的值甚至在应用程序编写时都没有填充.

Steven was right about the HttpContext being a runtime value. Its values are not even populated at the time the application is composed.

如果您考虑一下,这是有道理的,因为应用程序应该在任何单个用户上下文之外进行初始化.

This makes sense if you think about it because the application should be initialized outside of any individual user context.

然而,Steven 的解决方案只是将问题转移到了不同​​的服务.毕竟实现IUserContext的类还是需要把HttpContext作为依赖.

However, Steven's solution only moved the problem to a different service. After all, the class that implements IUserContext will still need to take HttpContext as a dependency.

解决方案是使用 Abstract Factory 来允许 HttpContext 实例在运行时访问而不是在工厂连接时访问.

The solution is to use an Abstract Factory to allow the HttpContext instance to be accessed at runtime instead of when the factory is wired up.

重要提示: HttpContext 不是抽象的,因此不能交换或模拟.为了确保我们处理的是抽象,微软提供了 HttpContextBase 抽象类和默认的具体类型 HttpContextWrapper.HttpContextBase 具有与 HttpContext 完全相同的接口.您应该始终使用 HttpContextBase 作为服务中的抽象引用类型,而不是 HttpContext.

Important: HttpContext is not an abstraction, so it cannot be swapped or mocked. To ensure we are dealing with an abstraction, Microsoft has provided the HttpContextBase abstract class and the default concrete type HttpContextWrapper. HttpContextBase has exactly the same interface as HttpContext. You should always use HttpContextBase as the abstract reference type within your services, not HttpContext.

考虑到这两点,您可以为您的 HttpContext 创建一个工厂,如下所示:

With those 2 things in mind, you can create a factory for your HttpContext, as follows:

public interface IHttpContextFactory
{
    HttpContextBase Create();
}

public class HttpContextFactory
    : IHttpContextFactory
{
    public HttpContextBase Create()
    {
        return new HttpContextWrapper(HttpContext.Current);
    }
}

然后可以修改您的 MembershipService 以在其构造函数中接受 IHttpContextFactory:

Your MembershipService can then be modified to accept an IHttpContextFactory in its constructor:

public class MembershipService : IMembershipService
{
    private readonly IHttpContextFactory httpContextFactory;

    // This is called at application startup, but note that it 
    // does nothing except get our service(s) ready for runtime.
    // It does not actually use the service.
    public MembershipService(IHttpContextFactory httpContextFactory)
    {
        if (httpContextFactory == null)
            throw new ArgumentNullException("httpContextFactory");
        this.httpContextFactory = httpContextFactory;
    }

    // Make sure this is not called from any service constructor
    // that is called at application startup.
    public void DoSomething()
    {
        HttpContextBase httpContext = this.httpContextFactory.Create();

        // Do something with HttpContext (at runtime)
    }
}

并且您只需要在组合时注入 HttpContextFactory.

And you need only inject the HttpContextFactory at composition time.

kernel.Bind<IHttpContextFactory>()
    .To<HttpContextFactory>();

kernel.Bind<IMembershipService>()
    .To<MembershipService>();

不过,仅此一项可能无法解决整个问题.您需要确保应用程序的其余部分在准备就绪之前不会尝试使用 HttpContext.就 DI 而言,这意味着您不能在应用程序启动中组合的任何类型的构造函数或这些构造函数之一调用的任何服务成员中使用 HttpContext.为了解决这个问题,您可能需要创建额外的抽象工厂,以确保这些服务在 HttpContext 准备就绪之前不会调用 IMembershipService 的成员.

This alone might not solve the entire issue, though. You need to ensure that the rest of your application does not try to use HttpContext before it is ready. In terms of DI, it means you can't use HttpContext in any constructor of types that are composed in application start or any service members that one of those constructors calls. To solve that, you may need to create additional abstract factories to ensure those services don't call members of IMembershipService until HttpContext is ready.

请参阅此答案,了解有关如何做到这一点.

See this answer for more information about how to accomplish that.

Steven 的解决方案还需要围绕 HttpContext 创建一个 Facade.虽然这并不能真正帮助解决手头的问题,但我同意如果您的 MembershipService(可能还有其他服务)只使用 HttpContext<的少数成员,这可能是个好主意/代码>.通常,此模式是为了使复杂对象更易于使用(例如将其扁平化为几个可能嵌套在其层次结构深处的成员).但是,您确实需要权衡添加另一种类型的额外维护与在应用程序中使用 HttpContext 的复杂性(或换出其中一部分的价值)来做出决定.

Steven's solution also entailed creating a Facade around HttpContext. While this does not really help solve the problem at hand, I agree that this might be a good idea if your MembershipService (and perhaps other services) only uses a small number of members of HttpContext. Generally, this pattern is to make a complex object simpler to work with (such as flattening it down to a few members that may be nested deep within its hierarchy). But you really need to weigh the extra maintenance of adding another type against the complexity of using HttpContext within your application (or the value of swapping out a section of it) to make that decision.

这篇关于MVC5 Ninject 绑定和 HttpContext的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆