c# - MVC5 Ninject 绑定(bind)和 HttpContext

标签 c# asp.net-mvc dependency-injection ninject httpcontext

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

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

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

但是在我使用 Ninject 模块的新项目中,在 StackOverflow 和 Google 上进行了一些搜索之后,我得出了以下代码: 公共(public)类 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();
        });

    }
}

但是,我得到以下错误:

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.

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

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

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

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

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

1) Request for HomeController

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

谢谢, 约翰

最佳答案

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

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

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

解决方案是使用 Abstract Factory允许在运行时而不是在工厂连接时访问 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 创建一个工厂,如下所示:

public interface IHttpContextFactory
{
    HttpContextBase Create();
}

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

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

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

并且您只需要在组合时注入(inject) HttpContextFactory

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

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

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

参见 this answer有关如何实现该目标的更多信息。

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

关于c# - MVC5 Ninject 绑定(bind)和 HttpContext,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28254247/

相关文章:

c# - 自定义过滤器属性注入(inject)依赖

java - 纯 Java 中的依赖注入(inject)

java - 使用注释处理延迟绑定(bind)到 Dagger2 Graph

c# - NuGet包库中的依赖注入(inject)

c# - Lambda 表达式获取表中的两列,但我如何使用 viewbag 在我的 View 中访问它

c# - POS协议(protocol)串口通讯

c# - 如何在退出时杀死线程?

c# - 如何使用泛型创建流畅的界面

c# - 为什么我的模型属性在尝试 POST 时为空?

asp.net-mvc - 如何在 View 中没有逻辑的情况下基于安全性隐藏 Razor View 上的特定元素?