RegisterInstance无法在Autofac中注册IntstancePerRequestMode

时间:2015-10-02 16:53:56

标签: asp.net-web-api autofac

我在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()共享。

这是什么意思?我不明白为什么我们有这个限制。但在任何情况下我的第一个目标 - 更新具有新依赖性的容器。 有人有想法吗?

1 个答案:

答案 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注册代理,而不是尝试更新现有容器或范围。使用阻力最小的路径,您将获得更多运气。