Dependency injection 在OWIN中间件中为每个请求配置Unity容器
我想根据HTTP请求的属性在ASP.NET Web API 2使用的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注册的类
/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中间件。上面的代码经过测试,它能够注册和解决依赖关系。更具体地说,您不能在操作过滤器中注册控制器构造函数依赖关系。这不是一个表演的停止,但它不是理想的。不过,我提出的其他问题是节目的停顿。