Dependency injection 在OWIN中间件中为每个请求配置Unity容器

Dependency injection 在OWIN中间件中为每个请求配置Unity容器,dependency-injection,asp.net-web-api2,unity-container,owin,Dependency Injection,Asp.net Web Api2,Unity Container,Owin,我想根据HTTP请求的属性在ASP.NET Web API 2使用的Unity容器中配置注册。例如,对/api/database1/values的请求应导致为database1配置IDbContext的Unity容器配置,而对/api/database4/values的请求将获得为database4配置的IDbContext 我已经使用了UnityHierarchicalDependencyResolver作为依赖项解析程序,因此使用HierarchyCallifeTimeManager注册的类

我想根据HTTP请求的属性在ASP.NET Web API 2使用的Unity容器中配置注册。例如,对
/api/database1/values
的请求应导致为database1配置
IDbContext
的Unity容器配置,而对
/api/database4/values
的请求将获得为database4配置的
IDbContext

我已经使用了
UnityHierarchicalDependencyResolver
作为依赖项解析程序,因此使用
HierarchyCallifeTimeManager
注册的类型仅在请求的生命周期内有效。这对于每个请求解析类型非常有效。但是,如何使用OWIN中间件在每个请求中注册它们,我无法理解

在我的中间件中,调用
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnityContainer))
会获取
IUnityContainer
的实例,但它对于所有请求都是相同的容器,包括来自以前请求的任何注册

通过使用我自己的
idependencysolver
实现来封装
UnityHierarchicalDependencyResolver
,我可以看到
idependencySolver.BeginScope
直到过程的后期才被调用。因此,问题似乎是,在我的中间件调用
Next(…)
很久之后,直到Web API唤醒,子容器才被创建

有没有办法让依赖项解析程序的作用域更快地启动?我还缺少其他策略吗。如果有什么不同的话,我将在IIS中托管,但更倾向于OWIN中间件方法

更新 这不是一个答案,它太大了,无法发表评论,但在与Unity一起努力解决这个问题后,我决定改用Autofac,一切都很顺利

Autofac OWIN包(
Autofac.Mvc5.OWIN
Autofac.OWIN
Autofac.WebApi2.OWIN
)使得在OWIN管道中使用Autofac非常容易,并确保在ASP.NET MVC和Web API中进行适当的生命周期管理。这是缺失的一环

我找不到一种方法来重新配置每个请求的容器,但它至少使每个请求配置一个工厂成为可能(是的,@Haukinger和@alltej,你朝这个方向努力是正确的)

所以我注册了一家工厂,比如:

builder.RegisterType<DataDependencyFactory>().InstancePerRequest();
builder
  .Register(c => c.Resolve<DataDependencyFactory>().CreateDataDependency())
  .As<IDataDependency>()
  .InstancePerRequest();
builder.RegisterType().InstancePerRequest();
并注册该工厂的创建方法,如:

builder.RegisterType<DataDependencyFactory>().InstancePerRequest();
builder
  .Register(c => c.Resolve<DataDependencyFactory>().CreateDataDependency())
  .As<IDataDependency>()
  .InstancePerRequest();
builder
.Register(c=>c.Resolve().CreateDataDependency())
.As()
.InstancePerRequest();
以这种方式注册工厂特别有用,因为下游依赖者不需要知道工厂。我喜欢这种方式,因为我的依赖者不需要工厂,他们需要一个实例。容器根据我的依赖者的需要而变化,而不是相反:)

然后,在一个OWIN中间件中,我解析工厂,并根据请求的属性设置工厂的属性。MVC或Web API控制器中的
IDataDependence
的后续解析,或OWIN管道中稍后的任何其他解析,将根据工厂上的属性配置一个实例。

基于您的API URL(“/API/database4/values”),我建议您创建一个过滤器属性(例如DbIdFilter)这样,您就可以将filter属性重用到遵循类似url路径/段的其他控制器方法,如下所示:

[HttpGet]
[DbIdFilter]
[Route("{databaseId}/values")]
public IHttpActionResult GetValues()
{
    return Ok();
}


[HttpGet]
[DbIdFilter]
[Route("{databaseId}/products")]
public IHttpActionResult GetProducts()
{
    return Ok();
}
首先,创建过滤器属性:

public class DbIdFilterAttribute : ActionFilterAttribute
{
    private readonly string _routeDataId;
    private const string defaultRouteName = "databaseId";
    public DbIdFilterAttribute():this(defaultRouteName)
    {}

    public DbIdFilterAttribute(string routeDataId)
    {
        _routeDataId = routeDataId;
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {

        var routeData = actionContext.Request.GetRouteData();

        var dbId = routeData.Values[_routeDataId] as string;

        //here we create the db instance at the filter level.

        DbInstanceFactory.RegisterDbInstance(dbId);


    }

}
接下来,创建一个实例工厂,该工厂将在运行时注册/解析db实例:

public class DbInstanceFactory : IDbInstanceFactory
{
    public static IDbInstance RegisterDbInstance(string databaseId)
    {
        var factory = UnityConfig.GetConfiguredContainer().Resolve<IDbInstanceFactory>();
        return factory.CreateInstance(databaseId);
    }

    public IDbInstance CreateInstance(string databaseId)
    {
        var container = UnityConfig.GetConfiguredContainer();
        //container.RegisterType<IDbInstance, DbInstance>();

        container.RegisterType<IDbInstance, DbInstance>(new InjectionConstructor(databaseId));
        var dbInstance = container.Resolve<IDbInstance>();
        return dbInstance;
    }

    public IDbInstance GetInstance()
    {
        var container = UnityConfig.GetConfiguredContainer();
        var dbInstance = container.Resolve<IDbInstance>();
        return dbInstance;
    }
}

public interface IDbInstanceFactory
{
    IDbInstance CreateInstance(string databaseId);
    IDbInstance GetInstance();
}
在控制器类中,您可以这样使用它:

....
private IDbInstanceFactory _dbFactory;

public MyController(IDbInstanceFactory dbFactory)
{
    _dbFactory = dbFactory;
}

// Alternate, if you want to use property injection instead of constructor injection
//[Dependency]
//public IDbInstanceFactory DbFactory { get; set; }

[HttpGet]
[DbIdFilter]
[Route("{databaseId}/test")]
public IHttpActionResult Test()
{
    var db = _dbFactory.GetInstance();

    return Ok(db.DbId);
}

...

FWIW,我觉得我没有试图做一些真正荒谬的事情,因为Autofac文档验证了这个概念。您可以实现一种自定义机制,该机制提供了根据每个请求注册和解析依赖项的能力。虽然我更愿意坚持统一,但如果这是一场艰苦的战斗,我会考虑一个替代的IOC容器,如AutoFac。你认为一个工厂用于你的代码> IDBWr原文创建上下文并获得相关的请求属性作为参数吗?这样,工厂只需要一次注册。工厂仍然需要每个请求的配置。这意味着它需要直接意识到连接(不理想),或者需要通过IoC容器注入某种连接上下文,这让我回到了起点。或者我遗漏了什么?不是真的,你必须以某种方式将连接链接到数据库上下文,无论是手动编码还是自动创建的。。。不过,我更喜欢手动方式,以减少对特定容器的依赖性。这在理论上是好的,但也有问题。1.
ActionFilterAttribute.OnActionExecuting
是在构造控制器之后执行的,因此在其中注册依赖项将不起作用。2.
ActionFilterAttribute
中的状态不是线程安全的,“任何实例成员都不能保证线程安全。”3。这是一个操作过滤器,而不是我的问题所寻求的OWIN中间件。上面的代码经过测试,它能够注册和解决依赖关系。更具体地说,您不能在操作过滤器中注册控制器构造函数依赖关系。这不是一个表演的停止,但它不是理想的。不过,我提出的其他问题是节目的停顿。