在WCF中请求完成后使用缓存的Autofac IComponentContext

时间:2015-11-25 18:24:34

标签: wcf dependency-injection autofac

我有以下WCF服务:

public class TimeService : ITimeService
{
    private FooFactory _fooFactory;
    public TimeService(FooFactory fooFactory)
    {
        _fooFactory = fooFactory;
    }

    public DateTime Now()
    {
        Foo result = null;
        Stopwatch sw = new Stopwatch();
        sw.Start();
        Debug.Print($"[{Thread.CurrentThread.ManagedThreadId}] BEGIN Operation");
        Task.Run<Foo>(() =>
        {
            Debug.Print($"[{Thread.CurrentThread.ManagedThreadId}] BEGIN Task {sw.ElapsedMilliseconds}ms");
            Task.Delay(TimeSpan.FromSeconds(10)).Wait();
            return _fooFactory.Create();
        }).ContinueWith(x =>
        {
            try
            {
                Debug.Print($"[{Thread.CurrentThread.ManagedThreadId}] CONTINUE Task {sw.ElapsedMilliseconds}ms");
                result = x.Result;
                Debug.Print($"[{Thread.CurrentThread.ManagedThreadId}] CONTINUE Task {sw.ElapsedMilliseconds}ms, Result = {result}");
            }
            catch (Exception ex)
            {
                Debug.Print($"[{Thread.CurrentThread.ManagedThreadId}] CONTINUE Task {ex}");
            }
        });
        Debug.Print($"[{Thread.CurrentThread.ManagedThreadId}] END Operation {sw.ElapsedMilliseconds}ms");
        return result?.Now ?? DateTime.MinValue;
    }
}

而且:

public class FooFactory
{
    private IComponentContext _componentContext;
    public FooFactory(Owned<IComponentContext> componentContext)
    {
        _componentContext = componentContext.Value;
    }
    public Foo Create()
    {
        return _componentContext.Resolve<Foo>();
    }
}

注册如下:

AutofacHostFactory.Container = CreateContainer();
...
private IContainer CreateContainer()
{
    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterType<Foo>().AsSelf();
    builder.RegisterType<FooFactory>().AsSelf().InstancePerLifetimeScope();
    //builder.Register(c => new FooFactory(c.Resolve<IComponentContext>()));
    builder.RegisterType<TimeService>().As<ITimeService>().AsSelf();
    return builder.Build();
}

基本上,我有一个操作立即返回给调用者,而我开始的任务在后台继续。令人惊讶的是,当请求“完成”时,这并没有终止任务 - 我本以为会这样做。

FooFactory需要动态创建Foo,这就是为什么它需要依赖IComponentContext(或ILifetimeScope - 有区别吗?)。 当我直接依赖IComponentContext或使用注释代码中的委托时,会发生一个异常,说明IComponentContext实例已被释放。我猜这是因为与请求相关联的父ILifetimeScope(通过Autofac.WCF)被处理掉了。但是,如果我依赖于Owned,则不会发生异常。所以看起来代码正在运行并做我想做的事。

然而,问题是,这样安全吗?

我的意思是,请求完成并且响应首先返回给调用者,之后实例解析从为请求创建的上下文发生。就Autofac而言,请求的范围已经消失。想象一下,当FooFactory调用来时,我们深深陷入了嵌套范围。

有什么影响?

1 个答案:

答案 0 :(得分:1)

这个问题有许多隐含的问题,并且不可能做出简单的回答。因此,我将尝试解释一些表达的疑虑。

首先:IComponentContext和ILifetimeScope之间的区别是什么? 至于this question

  

两个接口都提供标准的Res​​olve()操作   ILifetimeScope通过添加方法扩展IComponentContext   开始新的嵌套生命周期。

所以,鉴于不应该有任何性能损失,我总是使用ILifetimeScope,因为你可以根据需要打开嵌套范围。

要正确实例化组件,您需要一个有效的范围(或上下文,我会坚持使用&#34;范围&#34;对于其余的答案)。范围的好处在于,当Autofac将处理它们时,它将自动处理由它们解析的每个组件。

解析FooFactory的范围是WCF请求范围,它在请求完成时自动处理。

因此,您需要获取的例外情况是:上下文已被处理,无法再使用。

拥有和LT;&GT;正是出于这个目的而存在:为对象提供自治范围,因此当父范围关闭时它们不会被处理。

安全吗?是的,如果您在完成后致电处理,因为Autofac不知道何时需要处置。

作为参考(可能对其他读者而言),优秀的Autofac Lifetime Primer涵盖了您使用此工具时需要了解的所有内容。

让我补充一些关于实施可能存在缺陷的要点:

  • 你没有在FooFactory拥有的componentContext上调用Dispose()。所以你可能正在泄漏每个请求的上下文。并且,如果Foo应该被处置,那么没有人会这样做。
  • 第二:我注意到你注册了FooFactory为&#34; InstancePerLifetime&#34;。为什么?在我看来,工厂可以而且应该只被实例化一次。我会在&#34; SingleInstance&#34;。
  • 中更改它
  • 第三:你确定你需要FooFactory吗? Autofac已经为延迟初始化提供了一种简单的机制,这需要从Func&lt; componentType&gt;获取依赖性。 (在您的示例中为Func)或Owned&lt;&lt;&lt; Func&lt; Foo&gt;&gt;。它真的归结为可读性,所以这是值得商榷的。而不是要求FooFactory你需要一个委托Func&lt; Foo&gt;并在每次需要Foo时调用它。事实上,FooFactory可能需要一个Func&lt; Foo&gt;并且Create方法只会调用委托。
  • 第四,也就是最后。为了清理任务调用(并确保在需要时调用Dispose),我会更改它,考虑将任务绑定到Owned,这样就可以创建一个适当的Autofac范围,可以在其中解析组件。

只需要一个拥有的&lt; ILifetimeScope&gt;而不是FooFactory并在任务完成时处理它。 如果您需要进一步的建议,请告诉我。

祝你好运!