【问题标题】:Using Ninject with Owin and InRequestScope将 Ninject 与 Owin 和 InRequestScope 一起使用
【发布时间】:2014-12-04 20:57:11
【问题描述】:

我们正在尝试在带有 WebAPI 管道的 Owin 中使用 Ninject。我们根据this documentation 进行了所有设置,但我们无法让 InRequestScope() 工作。

这是 startup.cs 的重要部分

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();

        // Web API routes
        config.MapHttpAttributeRoutes();

        // Ninject Setup
        app.UseNinjectMiddleware(NinjectConfig.CreateKernel);
        app.UseNinjectWebApi(config);
    }

}

NinjectConfig 看起来像这样:

public sealed class NinjectConfig
{
    public static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();

        INinjectModule[] modules =
        {
            new ApplicationModule()
        };       

        instance.Load(modules);

        // Do we still need to do this wtih Owin?
        instance.Bind<IHttpModule>().To<OnePerRequestHttpModule>();
    }
}

我们的 ApplicationModule 位于一个单独的基础架构项目中,可以访问我们所有不同的层,用于处理 DI 和映射:

public class ApplicationModule: NinjectModule
{

    public override void Load()
    {
        // IUnitOfWork / EF Setups
        Bind<ApplicationContext>().ToSelf().InRequestScope();

        Bind<IUnitOfWork>().ToMethod(ctx => ctx.Kernel.Get<ApplicationContext>()});

        Bind<ApplicationContext>().ToMethod(ctx => ctx.Kernel.Get<ChromLimsContext>()}).WhenInjectedInto<IDal>();

        // other bindings for dals and business objects, etc.
    }
}

然后我们有几个接口:

public interface IUnitOfWork()
{
    void SaveChanges();

    Task SaveChangesAsync();
}

public interface IDal()
{
    // Crud operations, Sync and Async
}

然后我们的实际类使用这些:

public class SomeBusinessObject
{
    private IUnitOfWork _uow;
    private IDal _someDal;

    public SomeBusinessObject(IUnitOfWork uow, IDal someDal)
    {
        _uow = uow;
        _someDal = someDal;
    }

    public Task<SomeResult> SaveSomething(Something something)
    {
        _someDal.Save(something);
        _uow.SaveChanges();
    }
}

一些木豆

public class SomeDal : IDal {

    private ApplicationContext _applicationContext;

    public SomeDal(ApplicationContext applicationContext)
    {
        _applicationContext = applicationContext;
    }

    public void Save(Something something)
    {
        _applicationContext.Somethings.Add(something);
    }
}

我们的 EF DbContext

public class ApplicationContext : DbContext, IUnitOfWork
{
    // EF DBSet Definitions

    public void SaveChanges()
    {
        base.SaveChanges();
    }

}

期望对于每个请求,都会创建一个 ApplicationContext 实例,并将其作为 IUnitOfWork 实现注入到业务对象中,并作为 ApplicationContext 注入到 IDals 中。

相反,正在为每个使用它的类创建一个新的 ApplicationContext 实例。如果我将范围从 InRequestScope 切换到 InSingletonScope,那么(如预期的那样)为整个应用程序创建了一个实例,并正确地注入到指定的类中。既然这样可行,我假设这不是绑定问题,而是 InRequestScope 扩展的问题。

我能找到的与我遇到的类似的唯一问题是this one,但不幸的是,该解决方案不起作用。我已经引用了他在 WebApi 和 Infrastructure 项目中指定的所有包,我仔细检查以确保它们被复制到构建目录。

我做错了什么?

编辑: 一些额外的信息。查看 Ninject.Web.WebApi.OwinHost 和 Ninject.Web.Common.OwinHost 中的 Ninject 源代码,似乎 Owin 中间件将 OwinWebApiRequestScopeProvider 添加为 IWebApiRequestScopeProvider。然后在 InRequestScope() 扩展方法中使用此提供程序以返回名为“Ninject_WebApiScope”的命名范围。这将一直存在,直到被注入开关的目标类。然后命名的范围消失,并创建一个新的范围。我认为这可能是@BatteryBackupUnit 在他们的评论中所指的,但我不知道如何纠正它。

【问题讨论】:

  • 我猜是你的第二个Bind&lt;ApplicationContext&gt;() 导致了这个。您没有将其设置为 InRequestScope()
  • 不幸的是,这不起作用。我也试过只将 ApplicationContext 绑定到 InRequestScope 本身,然后只绑定 IUnitOfWork ToMethod(ctx => ctx.Kernel.Get()).InRequestScope()
  • .InRequestScope() 是否适用于 any 绑定?存在与 ninject nuget 包安装/升级相关的“已知”问题,其中未正确设置,并且未正确注册 Request-Scope 特定的 ninject 内容。 .InRequestScope() 没有效果 - 遗憾的是甚至没有抛出异常。
  • @BatteryBackupUnit 这是个好问题。不幸的是,我们所做的一切都是通过 Owin 管道进行的,因此 InRequestScope 的所有行为似乎都是一致的,也就是说它们没有按预期工作。
  • 我也看到过同样的行为。看起来 InRequestScope 在下面使用了一些 NamedScope 的东西。似乎正在发生的事情是它使用了一个名为“Ninject_WebApiScope”之类的范围,但是当它试图获取该范围时,它会抛出一个UnknownScopeException,这意味着它返回一个具有瞬态范围效果的空范围对象。应该在 WebApi 调用 BeginScope 时创建该命名范围,但由于某种原因它似乎不起作用。

标签: c# entity-framework asp.net-web-api ninject owin


【解决方案1】:

这个帖子与这个问题有关......

https://github.com/ninject/Ninject.Web.WebApi/issues/17

我发现 InRequestScope 的行为似乎会根据您注入它们的方式而改变。比如……

public ValuesController(IValuesProvider valuesProvider1, IValuesProvider valuesProvider2)
{
    this.valuesProvider1 = valuesProvider1;
    this.valuesProvider2 = valuesProvider2;
}

Ninject 将创建并注入相同的 IValuesProvider 实例。但是,如果方法写成...

/// <summary>
/// Initializes a new instance of the <see cref="ValuesController"/> class.
/// </summary>
/// <param name="valuesProvider">The values provider.</param>
public Values2Controller(IKernel kernel)
{
    this.valuesProvider1 = kernel.Get<IValuesProvider>();
    this.valuesProvider2 = kernel.Get<IValuesProvider>();
}

...这将创建两个新实例。

【讨论】:

  • 我们最终切换到 SimpleInjector,但这个答案是最好的,指出这是 Ninject 中的一个已知错误
【解决方案2】:

根据@Mick 帖子中链接中的信息,我最终添加了自己的扩展方法,如下所示。我不确定缺点。

public static class CustomRequestScope
{
    public static Ninject.Syntax.IBindingNamedWithOrOnSyntax<T> InCustomRequestScope<T>(this Ninject.Syntax.IBindingInSyntax<T> syntax)
    {
        return syntax.InScope(ctx => HttpContext.Current.Handler == null ? null : HttpContext.Current.Request);
    }
}

由于这个问题,我确实考虑切换到另一个容器。

【讨论】:

    猜你喜欢
    • 2013-12-27
    • 2020-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-28
    • 2011-10-18
    • 2011-02-06
    • 1970-01-01
    相关资源
    最近更新 更多