我在Web Api Delegating Handler中有一个代码,用于从请求标头中提取数据。 但是,我无法在Autofac容器中注册实例,因为Autofac容器只需要SingleInstance。
public class ExtractUserNameMessageHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var userNameFromFrontEnd = request.GetDependencyScope().GetService(typeof (IUserNameFromFrontEnd));
if (userNameFromFrontEnd == null)
{
var updatedContainerBuilder = new ContainerBuilder();
userNameFromFrontEnd = ExtractUserName(request);
if (userNameFromFrontEnd == null)
{
throw new Exception("We've got a request without UserName header");
}
updatedContainerBuilder.RegisterInstance(userNameFromFrontEnd)
.As<IUserNameFromFrontEnd>()
.InstancePerRequest();
var autofacDependencyResolver = GlobalConfiguration.Configuration.DependencyResolver as AutofacWebApiDependencyResolver;
if (autofacDependencyResolver == null)
{
throw new Exception("We can work with Autofac DI container");
}
updatedContainerBuilder.Update(autofacDependencyResolver.Container as IContainer);
}
当我尝试更新容器时,我收到一条带有消息的异常 - 注册只能支持singleinstance()共享。
这是什么意思?我不明白为什么我们有这个限制。但在任何情况下我的第一个目标 - 更新具有新依赖性的容器。 有人有想法吗?
答案 0 :(得分:2)
(注:This question was cross-posted to the Autofac forums as well.)
当您注册特定实例时,它实际上是一个单例 - 它是一个实例,即您提供的实例。
当您尝试分配InstancePerRequest
或实际上除了SingleInstance
之外的任何其他生命周期范围时,它都没有逻辑意义,因为您不会有所不同每个请求的实例(或其他)。您将获得您注册的完全相同的实例,这是一个单身人士。
异常消息试图告诉您如何避免不正确的期望:即使您告诉它,它也无法为每个请求提供不同的实例,因为您没有告诉它如何创建一个新实例,而您提供了一个特定的实例。
如果您需要每个生命周期范围/请求/不同的对象的不同实例,您需要注册一个类型,委托或其他告诉Autofac如何创建该新实例的内容。
这意味着如果您希望每个请求使用不同的IUserNameFromFrontEnd
,则需要将该逻辑移出DelegatingHandler
并转移到Autofac注册代理中。
// Make sure to register the HttpRequestMessage in the container
// so you can resolve it...
builder.RegisterHttpRequestMessage(httpConfiguration);
// Then, whilst building your root container...
builder
.Register(ctx =>
{
var request = ctx.Resolve<HttpRequestMessage>();
return ExtractUserName(request);
})
.As<IUserNameFromFrontEnd>()
.InstancePerRequest();
现在它可能会执行你想要做的事情 - 因为你告诉Autofac如何创建属于每个请求的实例。这也意味着你不再需要DelegatingHandler
,因为Autofac会做正确的事。
更高级(此处可能没用,但为了完整性):
如果出于某种原因,您仍然觉得需要直接在生命周期范围内修改注册,而不是更新容器,则应在创建请求生命周期范围时添加注册。
同样,不要更新每个生命周期范围或每个请求依赖项的根容器。它不会按您的想法工作。
创建新的生命周期范围时,您可以动态添加注册。
using(var scope = container.BeginLifetimeScope(
builder => builder.RegisterInstance(myfoo).As<IFoo>()))
{
// This will use the registrations in the container
// and the scope. f == myfoo
var f = scope.Resolve<IFoo>();
}
AutofacDependencyResolver
是创建请求生命周期范围并将其交给Web API的东西。 You can see the full source here.关键方法是BeginScope
:
public IDependencyScope BeginScope()
{
var lifetimeScope = _container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
return new AutofacWebApiDependencyScope(lifetimeScope);
}
如果您创建自己的AutofacDependencyResolver
,则可以修改范围的创建方式:
public IDependencyScope BeginScope()
{
var lifetimeScope = _container.BeginLifetimeScope(
MatchingScopeLifetimeTags.RequestLifetimeScopeTag,
builder => builder.RegisterInstance(myfoo).As<IFoo>());
return new AutofacWebApiDependencyScope(lifetimeScope);
}
这不是Autofac Web API集成中明确支持的扩展点 - 这就是您必须创建自己的解析器的原因。
然而,这似乎有点过分,无法解决您尝试解决的问题。 我强烈建议您只使用Autofac注册代理,而不是尝试更新现有容器或范围。使用阻力最小的路径,您将获得更多运气。