从MVC的DependencyResolver过渡到AutofacWebApiDependencyResolver - 哪里是.Current?

Sea*_*son 4 c# autofac asp.net-web-api

我让AutoFac与MVC4一起正常工作.我正在尝试过渡到Web API 2.这是我设置AutoFac的原因:

public class AutofacRegistrations
{
    public static void RegisterAndSetResolver()
    {
        // Create the container builder.
        var containerBuilder = new ContainerBuilder();

        // Register the Web API controllers.
        containerBuilder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        //  Only generate one SessionFactory ever because it is expensive.
        containerBuilder.Register(x => new NHibernateConfiguration().Configure().BuildSessionFactory()).SingleInstance();

        //  Everything else wants an instance of Session per HTTP request, so indicate that:
        containerBuilder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).InstancePerApiRequest();
        containerBuilder.Register(x => LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType)).InstancePerApiRequest();

        containerBuilder.RegisterType<NHibernateDaoFactory>().As<IDaoFactory>().InstancePerApiRequest();
        containerBuilder.RegisterType<StreamusManagerFactory>().As<IManagerFactory>().InstancePerApiRequest();

        // Build the container.
        ILifetimeScope container = containerBuilder.Build();

        // Create the depenedency resolver.
        var dependencyResolver = new AutofacWebApiDependencyResolver(container);

        // Configure Web API with the dependency resolver.
        GlobalConfiguration.Configuration.DependencyResolver = dependencyResolver;
    }
}
Run Code Online (Sandbox Code Playgroud)

我非常有信心所有这一切都是正确的.当我试图设置一些测试用例时,我的问题出现了.我的测试用例的基类不是控制器,因此它不会自动传递任何东西.在MVC4中,我做了以下事情:

[SetUp]
public void SetUp()
{
    HttpSimulator = new HttpSimulator().SimulateRequest();

    Logger = DependencyResolver.Current.GetService<ILog>();
    DaoFactory = DependencyResolver.Current.GetService<IDaoFactory>();
    Session = DependencyResolver.Current.GetService<ISession>();
    ManagerFactory = DependencyResolver.Current.GetService<IManagerFactory>();
}

[TearDown]
public void TearDown()
{
    HttpSimulator.Dispose();
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,WebAPI中没有DependencyResolver.Current.所以我想知道如何正确地做到这一点?

这构建,但不正确.我收到了"会话已关闭!"的消息.当我尝试执行测试用例时:

[SetUp]
public void SetUp()
{
    using (var scope = GlobalConfiguration.Configuration.DependencyResolver.BeginScope())
    {
        //  TODO: Consider initializing Helpers during setup to keep this DRY.
        Logger = (ILog)scope.GetService(typeof(ILog));
        DaoFactory = (IDaoFactory)scope.GetService(typeof(IDaoFactory));
        Session = (ISession)scope.GetService(typeof(ISession));
        ManagerFactory = (IManagerFactory)scope.GetService(typeof(IManagerFactory));
    }
}
Run Code Online (Sandbox Code Playgroud)

Tra*_*lig 5

使用WebAPI,您不需要访问当前的解析程序,因为当前的依赖项范围通常与入站一起出现HttpRequestMessage.该消息也是负责生成新请求范围的消息.

您可以在System.Net.Http.HttpRequestMessageExtensions.GetDependencyScope方法中查看此代码.

您在WebAPI中会注意到的一件大事是,您实际上并不需要在全局静态值中设置任何内容 - 也就是说,您不需要设置全局配置/解析器,因为现在所有内容都是基于实例的.

对于测试,这意味着:

  • 您的设置将HttpRequestMessage使用适当的依赖项解析程序和配置创建.
  • 你的拆解将处理HttpRequestMessage掉,这将依次处理依赖范围等.
  • HttpRequestMessage.GetDependencyScope()如果他们需要手动解决某些问题,将使用单独的测试,并且请求消息将用于协调/传递范围.

以更具体的方式,它可能看起来像:

private HttpRequestMessage _request;

[SetUp]
public void SetUp()
{
  var builder = new ContainerBuilder();
  // Register stuff.
  var container = builder.Build();
  var resolver = new AutofacWebApiDependencyResolver(container);

  var config = new HttpConfiguration();
  config.DependencyResolver = resolver;
  config.EnsureInitialized();

  this._request = new HttpRequestMessage();
  this._request.SetConfiguration(config);
}

[TearDown]
public void TearDown()
{
  this._request.Dispose();
}

[Test]
public void Test()
{
  // When you need to resolve something, use the request message
  this._request.GetDependencyScope().GetService(typeof(TheThing));
}
Run Code Online (Sandbox Code Playgroud)

这样做的好处在于,您不必在每次测试后都使用全局配置设置或重置静态值.

您可能想知道为什么要传递整个请求消息而不仅仅是依赖解析器 - 原因是请求消息是协调和控制依赖范围的生命周期的原因.否则,当您GetDependencyScope多次调用时,您将获得多个不同的范围,而不是您期望的范围.

从设计角度考虑的一些事项:

  • 您可能希望将事物的实际注册放入Autofac模块中,以便可以在测试和RegisterAndSetResolver方法中重复使用它,而不必担心全局静态被篡改.
  • RegisterAndSetResolver您可以考虑HttpConfigurationWebApiConfig.Register WebAPI为您提供的方法连接对象上设置解析器,而不是修改全局静态配置.将HttpConfiguration对象作为参数并在其上设置解析器而不是全局.
  • 如果您在单元测试中对所有内容进行了每次注册,那么您的单元测试可能更接近"集成测试".您可能会考虑仅查看您正在测试的内容所需的内容,并使用模拟框架来注册存根,而不是实际注册一大堆"真实内容".

无论如何,HttpRequestMessage是WebAPI的方法.