Замок Виндзор бросает "Тайник с областью действия уже уничтожен". в хабах SignalR

Мы разрабатываем веб-приложение на основе

  • .NET 4.5.1
  • MVC 5.2.2
  • Owin
  • WebApi 2.2
  • SignalR 2.2.0
  • Castle.Windsor 3.3.0
  • Wcf Integration Facility 3.3.0

Для разрешения контроллеров мы используем класс ControllerFactory, который был описан на странице ниже: http://docs.castleproject.org/Windsor.Windsor-tutorial-part-two-plugging-Windsor-in.ashx

Для разрешения зависимостей мы используем класс WindsorDependencyResolver:

public class WindsorDependencyResolver : IDependencyResolver
{
    public IWindsorContainer Container { get; private set; }

    public WindsorDependencyResolver(IWindsorContainer windsorContainer)
    {
        Container = windsorContainer;
    }

    public IDependencyScope BeginScope()
    {
        return new WindsorDependencyScope(this.Container);
    }

    public object GetService(Type serviceType)
    {
        return this.Container.Kernel.HasComponent(serviceType) ? this.Container.Resolve(serviceType) : null;
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return this.Container.ResolveAll(serviceType).Cast<object>().ToArray();
    }

    public void Dispose()
    {
    }
}

public class WindsorDependencyScope : IDependencyScope
{
    public IWindsorContainer Container { get; set; }
    public IDisposable Scope { get; set; }

    public WindsorDependencyScope(IWindsorContainer container)
    {
        this.Container = container;
        this.Scope = container.BeginScope();
    }

    public object GetService(Type serviceType)
    {
        return this.Container.Kernel.HasComponent(serviceType) ? this.Container.Resolve(serviceType) : null;
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return this.Container.ResolveAll(serviceType).Cast<object>().ToArray();
    }

    public void Dispose()
    {
        this.Scope.Dispose();
    }
}

Имейте в виду, что мы не разрешаем классы IHub SignalR с контейнером Windsor, они создаются системой OWIN в конвейере. Код Startup.cs показан ниже:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
        app.MapSignalR();
    }
}

Все контроллеры, клиенты службы wcf и перехватчики, за исключением классов ведения журналов, зарегистрированы в проекте LifestylePerWebRequest. Однако классы, которые мы используем для регистрации, являются одноэлементными.

В Web.config есть настройка ниже:

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
        ...
        <add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
        ...
    </modules>
</system.webServer>

Поэтому, когда мы пытаемся разрешить клиента wcf, который соответствует образу веб-запроса, в концентраторе SignalR, мы получаем следующее исключение:

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Scope cache was already disposed. This is most likely a bug in the calling code.'.
   at Castle.MicroKernel.Lifestyle.Scoped.ScopeCache.get_Item(Object id)
   at Castle.MicroKernel.Lifestyle.Scoped.DefaultLifetimeScope.GetCachedInstance(ComponentModel model, ScopedInstanceActivationCallback createInstance)
   at Castle.MicroKernel.Lifestyle.ScopedLifestyleManager.Resolve(CreationContext context, IReleasePolicy releasePolicy)
   at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired, Burden& burden)
   at Castle.MicroKernel.Handlers.DefaultHandler.Resolve(CreationContext context, Boolean instanceRequired)
   at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context)
   at Castle.MicroKernel.DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments, IReleasePolicy policy)
   at Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy)
   at Castle.MicroKernel.DefaultKernel.Resolve(Type service, IDictionary arguments)
   at Castle.Windsor.WindsorContainer.Resolve[T]()
   at UIServer.WebUI.Hubs.MailThreadHub.Broadcast(MailMessageListDto mailMessage) in c:\Development\DDD\UIServer.WebUI\Hubs\MailThreadHub.cs:line 92

Я вижу HttpContext в окне отладчика до вызова метода Container.Resolve(). Кстати, я могу разрешить классы одиночной регистрации.

Интересно, что мой товарищ по команде не получает никаких исключений. Основное отличие заключается в наших версиях ОС. Я запускаю код в Windows 8.1, и мой товарищ по команде запускает его в Windows 7.

Мы получаем это исключение только для концентраторов сигналов. Мы не получаем никаких исключений в любом другом месте. Как мы можем решить эту проблему?

1 ответ

Я использую ServiceLocator для разрешения IDependencyResolver в классе запуска. Это выглядит как:

internal class Startup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        var config = new HttpConfiguration();
        config.Routes.MapHttpRoute("ActionApi", "{controller}/{action}/{id}", new {id = RouteParameter.Optional});

        config.DependencyResolver = ServiceLocator.Instance.Resolve<IDependencyResolver>();

        appBuilder.UseWebApi(config);
    }
}

Может быть, это помощь.

Другие вопросы по тегам