【问题标题】:SimpleInjector.ActivationException with Simple Injector on SignalrRSignalrR 上带有简单注入器的 SimpleInjector.ActivationException
【发布时间】:2015-11-04 10:28:30
【问题描述】:

我在 Using Simple Injector with SignalR 上实现了答案,并且我的服务已成功解析,直到在 Hub 类上调用 OnDisconnected。然后我不得不按照这个问题Simple Injector per-web-api-request dependency in SignalR hub 作为一种解决方法,但只要请求集线器实例就会出现异常。

我得到异常说:

[SimpleInjector.ActivationException] ChatHub 类型的注册委托引发了异常。 ChatHub 注册为“混合 Web 请求/执行上下文范围”生活方式,但在混合 Web 请求/执行上下文范围的上下文之外请求实例。

堆栈跟踪:

at SimpleInjector.InstanceProducer.GetInstance()
at SimpleInjector.Container.GetInstance(Type serviceType)
at QuickChat.Hubs.SimpleInjectorHubActivator.Create(HubDescriptor descriptor) in c:\Users\Peter\Documents\Visual Studio 2013\Projects\QuickChat\QuickChat\Hubs\SimpleInjectorHubActivator.cs:line 21
at Microsoft.AspNet.SignalR.Hubs.DefaultHubManager.ResolveHub(String hubName)
at Microsoft.AspNet.SignalR.Hubs.HubDispatcher.CreateHub(IRequest request, HubDescriptor descriptor, String connectionId, StateChangeTracker tracker, Boolean throwIfFailedToCreate)

InnerException: 

at SimpleInjector.Scope.GetScopelessInstance[TService,TImplementation](ScopedRegistration`2 registration)
at SimpleInjector.Scope.GetInstance[TService,TImplementation](ScopedRegistration`2 registration, Scope scope)
at SimpleInjector.Advanced.Internal.LazyScopedRegistration`2.GetInstance(Scope scope)
at lambda_method(Closure )
at SimpleInjector.InstanceProducer.GetInstance()

请参阅下面我当前的代码配置。 集线器激活器:

public class SimpleInjectorHubActivator : IHubActivator
{
    private readonly Container _container;

    public SimpleInjectorHubActivator(Container container)
    {
        _container = container;
    }

    public IHub Create(HubDescriptor descriptor)
    {
        return (IHub)_container.GetInstance(descriptor.HubType);
    }
}

SimpleInjector 服务注册:

public class SimpleInjectorConfig
{
    public static void Register()
    {
        // Create the container as usual.
        var container = new Container();

        var hybrid = Lifestyle.CreateHybrid(
            () => container.GetCurrentExecutionContextScope() != null,
            new SimpleInjector.Integration.Web.WebRequestLifestyle(),
            new ExecutionContextScopeLifestyle());

        // Register types:
        container.RegisterSingle<MembershipRebootConfiguration>(MembershipRebootConfig.Create);
        container.Register<DefaultMembershipRebootDatabase>(() => new CustomMembershipRebootDatabase());
        container.Register<UserAccountService>(() => new UserAccountService(container.GetInstance<MembershipRebootConfiguration>(), container.GetInstance<IUserAccountRepository>()));
        container.Register<AuthenticationService, SamAuthenticationService>();
        container.RegisterPerWebRequest<IUserAccountQuery, DefaultUserAccountRepository>();
        container.RegisterPerWebRequest<IUserAccountRepository, DefaultUserAccountRepository>();

        container.Register(() => new DataAccess.EF.DataContext(), hybrid);
        container.Register<IUnitOfWork, UnitOfWork>(hybrid);
        container.Register<IUserService, UserService>(hybrid);

        //Register SimpleAuthentication callback provider class
        container.RegisterPerWebRequest<IAuthenticationCallbackProvider, SimpleAuthenticationProviderController>();
        //Register SimpleAuthentication MVC controller.
        container.RegisterPerWebRequest<SimpleAuthenticationController>(
            () => new SimpleAuthenticationController(container.GetInstance<IAuthenticationCallbackProvider>(), null));

        // This is an extension method from the integration package.
        container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
        // This is an extension method from the integration package as well.
        container.RegisterMvcIntegratedFilterProvider();
        //Enable injections to SignalR Hubs
        var activator = new SimpleInjectorHubActivator(container);
        container.Register<ChatHub, ChatHub>(hybrid);
        GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => activator);

        container.Verify();
        //Set dependency resolver for MVC
        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
    }
}

Global.asax.cs:

protected void Application_Start()
    {
        SimpleInjectorConfig.Register();
        // Register the default hubs route: ~/signalr/hubs
        RouteTable.Routes.MapHubs();
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
    }

【问题讨论】:

    标签: c# dependency-injection signalr signalr-hub simple-injector


    【解决方案1】:

    你的混合生活方式是错误的。你应该把谓词转过来:

    var hybrid = Lifestyle.CreateHybrid(
        () => container.GetCurrentExecutionContextScope() != null,
        new ExecutionContextScopeLifestyle(),
        new SimpleInjector.Integration.Web.WebRequestLifestyle());
    

    提示:您也可以像这样将其设置为默认作用域生活方式,而不是在整个组合根中重复使用 hybrid 变量:

    container.Options.DefaultScopedLifestyle = hybrid;
    

    这样您就可以将您的注册更改为以下内容:

    container.Register<ChatHub, ChatHub>(Lifestyle.Scoped);
    

    这使您的注册更轻松、更清晰。

    【讨论】:

    • 我仍然是一个错误,但这只是像你说的那样交换谓词,并且没有 DefaultScopedLifestyle 代码语句。我需要安装这个包SimpleInjector.Extensions.LifetimeScoping 吗?我找不到Lifestyle.Scoped 和 DefaultScopedLifestyle 的定义。
    • Lifestyle.ScopedOptions.DefaultScopedLifestyle 是 Simple Injector v3 中的新功能。您可能仍在使用 v2,不是吗?在您的情况下,您根本不需要LifetimeScoping。注意Lifestyle.ScopedOptions.DefaultScopedLifestyle 的使用是完全可选的;它只是更清洁 IMO。
    • 新的异常完全一样吗?
    • 只有在用户与 Hub 断开连接时才会出现该错误
    • @pmbanugo:啊……这是个老问题。看看this answer。它准确地解释了问题和解决方案。
    【解决方案2】:

    就像 Steven 在他的回答中所说,我的混合生活方式是错误的。我不得不改变谓词:

    var hybrid = Lifestyle.CreateHybrid(
    () => container.GetCurrentExecutionContextScope() != null,
    new ExecutionContextScopeLifestyle(),
    new SimpleInjector.Integration.Web.WebRequestLifestyle());
    

    在与 Steven GitHub 进行了几次讨论后,我得出了一个结论。

    我必须将我的ChatHub 设为Humble Object 并从中提取所有逻辑并将其放入一个单独的类中,该类实现一个服务接口,该接口由公开最初包含在ChatHub 中的所有逻辑的方法组成

    public interface IChatService
    {
        void OnConnected(Guid userId, string connectionId, HubConnectionContext clients);
        void OnDisconnected(Guid userId, string connectionId);
        void Broadcast(string message, string username, HubConnectionContext clients);
    }
    
    public class ChatService : IChatService
    {
        private readonly IUnitOfWork _unitOfWork;
        public UserChatService(IUnitOfWork unitOfWork) {
            _unitOfWork = unitOfWork;
        }
    
        public void OnConnected(Guid userId, string connectionId, HubConnectionContext clients) { 
            //Perform other operation using _unitOfWork
        }
        public void OnDisconnected(Guid userId, string connectionId) {
            //Perform other operation using _unitOfWork
        }
        public void Broadcast(string message, string username, HubConnectionContext clients) { 
            //Perform other operation using _unitOfWork and HubConnectionContext
            //broadcast message to other connected clients
            clients.others.broadcast(message);
        }
    }
    

    HubConnectionContext 对我来说确实像是运行时数据,所以我决定将它作为参数传递给方法。

    这样,我的集线器ChatHub 看起来很轻巧,将调用委托给ChatService 对象,并且在调用集线器的OnDisconnected() 时我不再遇到错误。

    别忘了用容器注册你的服务:

    container.Register<IChatService, ChatService>(hybrid);
    

    【讨论】:

    • 这个(和 Github 链接)形成了一个极好的答案 - 我遇到了同样的问题,并花时间构建我的 Hub 实现,正确地整理出抛出的 OnDisconnect 异常。感谢您提供完整的示例。
    • pmbanugo,你有完整的例子吗?我被困在这个谢谢你
    • @rsegovia 我创建了这个github gist。它应该有帮助。如果对您有帮助,请不要忘记对问题和答案进行投票。祝你好运..
    猜你喜欢
    • 1970-01-01
    • 2016-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多