【问题标题】:Captive Dependency with AutofacAutofac 的强制依赖
【发布时间】:2017-03-24 14:14:50
【问题描述】:

我正在寻找使用 autofac 解决 Captive Dependency 问题的最简洁方法。

我有一个短期课程,将根据 LifeTimeScope 注册:

public class ShortLived
{
    public void DoSomethingUsefull() {}
}

我有一个长寿类,它将被注册为单个实例。它依赖于 ShortLived 类:

public class LongLived
{
    private readonly Func<ShortLived> _getCurrentShortLived;

    public LongLived(Func<ShortLived> getCurrentShortLived)
    {
        _getCurrentShortLived = getCurrentShortLived;
    }

    public void DoSomethingWithShortLived()
    {
        var currentShortLived = _getCurrentShortLived();
        currentShortLived.DoSomethingUsefull();
    }
}

以下尝试无效。它会抛出 Autofac.Core.DependencyResolutionException。

public void CaptiveDependencyTest()
{
    var builder = new ContainerBuilder();
    builder.RegisterType<LongLived>()
        .SingleInstance();
    var container = builder.Build();

    using (var scope = container.BeginLifetimeScope(b => b.RegisterType<ShortLived>()))
    {
        var longLived = scope.Resolve<LongLived>();
        longLived.DoSomethingWithShortLived();
    }
}

以下确实有效。但我真的希望有一个比依赖某种静态变量更好的解决方案。

private static ILifetimeScope currentLifetimeScope;

public void CaptiveDependencyTest2()
{
    var builder = new ContainerBuilder();
    builder.Register(c =>
    {
        Func<ShortLived> shortLivedFacotry = () => currentLifetimeScope.Resolve<ShortLived>();
        return new LongLived(shortLivedFacotry);
    })
    .SingleInstance();
    var container = builder.Build();

    using (var scope = container.BeginLifetimeScope(b => b.RegisterType<ShortLived>()))
    {
        currentLifetimeScope = scope;
        var longLived = scope.Resolve<LongLived>();
        longLived.DoSomethingWithShortLived();
    }
}

一些背景信息: 我正在开发 OWIN 托管的 ASP.Net WebApi2 微服务。调用其他服务时,我需要从 currentOwinContext.Request.User.Identity 读取值并将它们添加到我发送给下一个服务的 RequestMessage 中。我的 LongLived 类是一个 DelegatingHandler(即 HttpClient“HttpMessageHandler-Pipeline”的一部分),并且 HttpClient 需要是 .SingleInstance() 所以我不必为我发出的每个请求实例化新的 HttpClients。 ShortLived 类是 IOwinContext,它注册在 Owin Pipeline 的 LifeTimeScope 中。

我可以在 autofac 中注册 HttpConfiguration,而不是 currentLifeTimeScope 的静态变量。然后我可以使用 httpConfig.DependencyResolver.GetRequestLifetimeScope(); 获得 currentLifeTimeScope我还没有测试过这种方法。我仍然希望找到更干净的东西。

【问题讨论】:

    标签: c# autofac


    【解决方案1】:

    如果类型 T 注册到容器中,Autofac 将自动解析对 Func 的依赖,作为通过容器创建 T 实例的工厂,请查找有关委托工厂的更多信息

    如下所示注册您的 ShortLived 课程,现在一切正常。

    public void CaptiveDependencyTest()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<ShortLived>()
            .SingleInstance();
        builder.RegisterType<LongLived>()
            .SingleInstance();
        var container = builder.Build();
    
        //using (var scope = container.BeginLifetimeScope(b => b.RegisterType<ShortLived>()))
        //{
            var longLived = container.Resolve<LongLived>();
            longLived.DoSomethingWithShortLived();
        //}
    }
    

    你也可以直接从容器中解析,不再需要使用,但最终选择是你的

    【讨论】:

    • 你已经把短暂的依赖变成了单例?我认为您不理解“强制依赖”的概念——长期存在的组件会保留对短期组件的引用,从而使该组件也长期存在(通常是无意的)。如果您将两个组件都设为单例,则“强制依赖”甚至不适用,并且您还没有解决 OP 的问题。
    【解决方案2】:

    ASP.NET Core 中,HttpContext 类提供属性 public IServiceProvider RequestServices { get; set; }。即使您只有对根生命周期范围的引用,这也可用于解决请求生命周期范围的依赖关系。诀窍是记住ILifetimeScope 可以从任何IComponentContext 解析。这种行为可以封装在下面的扩展方法中。

    public static ILifetimeScope GetLifetimeScope(this HttpContext httpContext)
    {
       return httpContext.RequestServices.GetService(typeof(ILifetimeScope)) as ILifetimeScope;
    }
    

    现在您可以使用以下代码轻松进入“每个请求”生命周期范围,给定IComponentContext 的实例。

    ctx => {
      var requestScope = ctx.Resolve<IHttpContextAccessor>().HttpContext.GetLifetimeScope();
    
      var shortLived = requestScope.Resolve<ShortLived>();
    }
    


    在传统的 ASP.NET 中,存在“依赖解析器”而不是“服务提供者”。

    例如GlobalConfiguration.Configuration.DependencyResolver = 在这里集成 autofac

    解决ILifetimeScope 有点麻烦。

    public static ILifetimeScope GetLifetimeScope(this HttpContext httpContext)
    {
        var requestMessage = httpContext.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
    
        return requestMessage?.GetDependencyScope().GetService(typeof(ILifetimeScope)) as ILifetimeScope;
    }
    
    var requestScope = HttpContext.Current.GetLifetimeScope();
    
    var shortLived = requestScope.Resolve<ShortLived>();
    

    【讨论】:

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