2015-11-25 2 views
2

У меня есть следующие службы WCF:Использование кэшируются Autofac IComponentContext после завершения запроса в 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

В этом вопросе есть много неявных вопросов, и простой ответ невозможен. Поэтому я попытаюсь объяснить некоторые из высказанных сомнений.

Во-первых: в чем разница между IComponentContext и ILifetimeScope? Что касается this question,

Оба интерфейса обеспечивают стандартные Resolve() операции, в то время как ILifetimeScope расширяет IComponentContext путем добавления методов для запуска новых вложенных жизней.

Итак, учитывая, что не должно быть никаких штрафных санкций за производительность, я всегда буду использовать ILifetimeScope, потому что при необходимости вы можете открыть вложенные области.

Для правильной компоновки компонентов вам нужна действительная область действия (или контекст, я останусь с «областью» для остальной части ответа). Самое приятное в облаках - это то, что когда Autofac будет их уничтожать, он автоматически удалит каждый разрешенный ими компонент.

Область разрешения, в которой разрешена FooFactory, представляет собой область запроса WCF, которая автоматически удаляется, когда запрос завершается.

Таким образом, исключение, в котором ожидается получение: контекст был удален и больше не может использоваться.

Для этой цели существует < >: для предоставления объектов с автономной областью, поэтому они НЕ удаляются при закрытии родительской области.

Это безопасно? ДА, если вы завершите вызов, когда Autofac не узнает, когда это необходимо.

В качестве справки (возможно, для других читателей) отличный Autofac Lifetime Primer охватывает все, что вам нужно знать об использовании этих инструментов.

Позвольте мне добавить некоторые моменты, о возможных недостатках вашей реализации:

  • вы не вызывая Dispose() на принадлежащем componentContext FooFactory в. Таким образом, вы, вероятно, утечка контекста для каждого запроса. И, если Foo должен быть ликвидирован, никто не собирается это делать.
  • Во-вторых: я заметил, что вы зарегистрировали FooFactory как «экземпляр экземпляра». Зачем? Как я вижу, фабрика может и должна быть создана только один раз. Я бы изменил его в «SingleInstance».
  • В-третьих: вы уверены, что вам нужен FooFactory? Autofac уже предоставляет простой механизм для ленивой инициализации, который требует принятия зависимости от Func <componentType> (в вашем примере Func) или принадлежит < < Func <Foo> >. Это действительно сводится о читаемости, так это спорно. Вместо того, чтобы требовать FooFactory, вам нужен делегат Func <Foo> и называть его каждый раз, когда вам нужен Foo. Фактически, FooFactory может потребовать Func <Foo>, а метод Create просто вызовет делегат.
  • Четвертый и последний. Чтобы очистить вызов задачи (и обеспечить, чтобы Dispose вызывается, если и когда это необходимо), я бы изменил его, рассматривая возможность привязки задачи к Owned, таким образом вы создаете правильную область Autofac, в которой компоненты могут быть разрешены.

Просто требуют имено <ILifetimeScope> вместо того, чтобы FooFactory и утилизируйте его, когда задача выполнена. Если вам нужны дальнейшие советы, дайте мне знать.

Удачи вам!

Смежные вопросы