【问题标题】:Simple Injector - Constructor Inject the current Web API request's HttpRequestMessageSimple Injector - 构造函数注入当前 Web API 请求的 HttpRequestMessage
【发布时间】:2017-06-08 04:51:34
【问题描述】:

在我的 Web API 项目中,我有一个需要当前请求的依赖项。

代码如下:

public interface IResourceLinker {
  Uri Link(string routeName, object routeValues);
}

public class ResourceLinker : IResourceLinker {
  private readonly HttpRequestMessage _request;

  public ResourceLinker(HttpRequestMessage request) {
    _request = request;
  }

  public Uri Link(string routeName, object routeValues) {
    return new Uri(_request.GetUrlHelper()
        .Link(routeName, routeValues));
  }
}

public class TestController : ApiController {
  private IResourceLinker _resourceLinker;

  public TestController(IResourceLinker resourceLinker) {
    _resourceLinker = resourceLinker
  }

  public Test Get() {
    var url = _resourceLinker.Link("routeName", routeValues);

    // etc.
  }
}

使用Simple Injector,是否可以在运行时将当前请求注入到容器中?

我尝试了以下方法:

public class InjectRequestHandler : DelegatingHandler
{
  protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
  {
    InjectRequest(request);
    return base.SendAsync(request, cancellationToken);
  }

  public static void InjectCurrentRequestIntoContainer(
    HttpRequestMessage request)
  {
    var resolver = (SimpleInjectorDependencyResolver)
      request.GetDependencyScope();
    resolver.Container.Register(() => request);
  }
}

但收到以下错误

第一次调用 GetInstance、GetAllInstances 和 Verify 后,容器无法更改。

有没有办法在运行时将当前请求注入到容器中?

【问题讨论】:

  • 为什么不在 HttpRequestMessage 上将链接方法定义为扩展方法并在 TestController 中调用 this.Request.Link (...)?否则,由于 HttpRequestMessage 是运行时值,请将其作为方法参数传递给您的 Link 方法,而不是使用构造函数注入。
  • 史蒂文,感谢您的意见。作为simpleinjector的创造者,是否可以运行时注册?
  • 从容器中解析出第一个实例后,不能添加任何新的注册。但即使是这样,在容器中注册 HttpRequestMessage 也会导致竞争条件,因为容器是全局的,但每个请求都需要一条消息。
  • Simple Injector 2.5 现在包含一个 Web API 集成包,它包含一个 GetCurrentHttpRequestMessage() 扩展方法,允许您从容器中检索当前消息。

标签: c# .net dependency-injection simple-injector


【解决方案1】:

容器会在注册阶段之后阻止任何注册。这将容器的使用分为两个阶段:注册和解析。 Simple Injector 不是唯一这样做的容器。例如,Autofac 通过允许用户使用 ContainerBuilder 进行注册来更明确地做到这一点,该方法使用 Build 方法构建容器作为配置阶段的最后一步。

Simple Injector 不允许这样做,主要是因为在应用程序生命周期的后期进行注册很容易导致各种有问题的行为,例如竞争条件。这也使得 DI 配置更难理解,因为注册分散在整个应用程序中,而您应该在应用 DI 时尝试集中注册。作为一个副作用,这种设计允许容器在多线程场景中具有线性性能特征,因为容器的快乐路径没有锁。

Simple Injector 允许使用 ResolveUnregisteredType 事件进行即时注册,但这不是您的情况。

您遇到的问题是您想将仅在运行时才知道的对象注入对象图中。这可能不是最好的做法,因为通常您应该通过方法参数传递运行时依赖项,而编译时/配置时依赖项应该通过构造函数传递。

但是,如果您确定将 HttpRequestMessage 作为构造函数参数传递是正确的做法,您需要做的是在请求的生命周期内缓存此消息并进行允许返回的注册缓存实例。

这就是它的样子:

// using SimpleInjector.Advanced; // for IsVerifying()

container.Register<HttpRequestMessage>(() =>
{
    var context = HttpContext.Current;

    if (context == null && container.IsVerifying())
        return new HttpRequestMessage();

    object message = context.Items["__message"];
    return (HttpRequestMessage)message;
});

此注册将检索缓存在 HttpContext.Items 字典中的 HttpRequestMessage。在验证期间(调用container.Verify() 时)有一个额外的检查以允许此注册工作,因为那时没有HttpContext.Current。但是,如果您对验证容器不感兴趣(通常是 really should btw),您可以将其最小化为:

container.Register<HttpRequestMessage>(() =>
    (HttpRequestMessage)HttpContext.Current.Items["__message"]);

有了这个注册,你唯一需要做的就是缓存一个HttpRequestMessage实例之前container.GetInstance被调用来获取控制器:

public static void InjectCurrentRequestIntoContainer(
    HttpRequestMessage request)
{
    HttpContext.Current.Items["__message"] = request;
}

更新

Web API Integration package 包含一个GetCurrentHttpRequestMessage 扩展方法,允许您检索当前请求的HttpRequestMessage。 Web API 集成 Wiki 页面describes 如何使用此扩展方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多