35

自分のIoCを使用することは、SignalRを使用するのは非常に簡単だと思いましたが、おそらくそうです。おそらく私は何か間違ったことをしている。これが私がこれまでに持っている私のコードです:

private static void InitializeContainer(Container container)
{

   container.Register<IMongoHelper<UserDocument>, MongoHelper<UserDocument>>();
   // ... registrations like about and then:
   var resolver = new SimpleInjectorResolver(container);
   GlobalHost.DependencyResolver = resolver;
}

そして私のクラス:

public class SimpleInjectorResolver : DefaultDependencyResolver
{
    private Container _container;
    public SimpleInjectorResolver(Container container)
    {
        _container = container;
    }

    public override object GetService(Type serviceType)
    {
        return _container.GetInstance(serviceType) ?? base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.GetAllInstances(serviceType) ?? base.GetServices(serviceType);
    }
}

最終的には、IJavaScriptProxyGeneratorを解決できないというエラーが発生するので、登録を追加します。

container.Register<IJavaScriptProxyGenerator, DefaultJavaScriptProxyGenerator>(
    ConstructorSelector.MostParameters);

しかし、他にもたくさんあります!私は次のようになります:

container.Register<IDependencyResolver, SimpleInjectorResolver>();
container.Register<IJavaScriptMinifier, NullJavaScriptMinifier>();
container.Register<IJavaScriptProxyGenerator, DefaultJavaScriptProxyGenerator>(
    ConstructorSelector.MostParameters);
container.Register<IHubManager, DefaultHubManager>();
container.Register<IHubActivator, DefaultHubActivator>();
container.Register<IParameterResolver, DefaultParameterResolver>();
container.Register<IMessageBus, InProcessMessageBus>(ConstructorSelector.MostParameters);

ITraceManagerそれでも「タイプの登録が見つかりませんでした」と表示されます。...しかし、SignalRが実行しているすべてのことを再配線する必要がないことを期待しているので、これを正しく実行しているかどうか疑問に思っています...そうですか?うまくいけば?そうでなければ、私は歩き続けますが、私はSignalRとSimple Injectorの初心者なので、最初に聞いてみようと思いました。:)

追加:SignalRには複数のコンストラクターがあったため、https ://cuttingedge.it/blogs/steven/pivot/entry.php?id=88。

4

6 に答える 6

51

さて、私は昨日試しました、そして私は解決策を見つけました。私によると、SignalRに依存性注入が必要なのは、ハブだけです。SignalRが内部でどのように機能しているかは気にしません。したがって、DependencyResolverを置き換える代わりに、IHubActivatorの独自の実装を作成しました。

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);
    }
}

このように登録できること(Application_Startで):

var activator = new SimpleInjectorHubActivator(container);
GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => activator);
RouteTable.Routes.MapHubs();
于 2013-04-02T21:46:30.000 に答える
26

SimpleInjectorまたは別のIoCを使用して、SignalRで依存性注入を使用する独自の方法を見つけるのに役立つ、他の回答とともに2セントをここに投入したいと思います。

@Stevenの答えを使用する

スティーブンの回答を使用する場合は、ルートを作成する前にハブルートを登録してください。SignalRRouteExtensions.MapHubs拡張メソッド(別名routes.MapHubs())は、ハブルートをマッピングするときにを呼び出すためRegister(Type, Func<object>)GlobalHost.DependencyResolverルートがマッピングされる前にDefaultDependencyResolverスティーブンとスワップアウトすると、スティーブンに遭遇します。SimpleInjectorResolverNotSupportedException

@NathanaelMarchandの回答を使用する

これは私のお気に入りです。なんで?

  1. より少ないコードSimpleInjectorDependencyResolver
  2. DefaultDependencyResolver(aka )を置き換える必要はありませんGlobalHost.DependencyResolver。つまり、コードがさらに少なくなります。
  3. ルートは、ハブルートをマッピングする前でも後でも作成できますDefaultDependencyResolver。これは、を置き換えていないため、「正常に機能」します。

ナタナエルが言ったように、これはクラスへの依存関係を気にする場合にのみ当てはまりますHub。これはおそらくほとんどの場合に当てはまります。SignalRに他の依存関係を注入することをいじりたい場合は、Stevenの答えを使用することをお勧めします。

Webリクエストごとの依存関係に関する問題Hub

SignalRには興味深い点があります...クライアントがハブから切断すると(たとえば、ブラウザウィンドウを閉じることによって)、Hubを呼び出すためにクラスの新しいインスタンスが作成されますOnDisconnected()。これが発生すると、HttpContext.Currentはnullになります。したがって、これHubにWebリクエストごとに登録されている依存関係がある場合、何かがうまくいかない可能性があります。

Ninjectで

nugetでNinjectとninjectSignalr依存性リゾルバーを使用してSignalR依存性注入を試しました。この構成では、切断イベント中に.InRequestScope()に注入されると、バインドされた依存関係が一時的に作成されます。はnullなので、Ninjectはそれを無視して、通知せずに一時的なインスタンスを作成することを決定したと思いますHubHttpContext.Currentおそらく、これについて警告するようにninjectに指示する構成設定がありましたが、これはデフォルトではありませんでした。

SimpleInjectorで

一方、SimpleInjectorは、にHub登録されているインスタンスに依存している場合、例外をスローしWebRequestLifestlyleます。

タイプNameOfYourHubの登録済みデリゲートが例外をスローしました。タイプNameOfYourPerRequestDependencyの登録済みデリゲートが例外をスローしました。YourProject.Namespace.NameOfYourPerRequestDependencyは「PerWebRequest」として登録されていますが、インスタンスはHttpContextのコンテキスト外で要求されています(HttpContext.Currentはnullです)。このライフスタイルを使用するインスタンスが、アプリケーションの初期化フェーズ中およびバックグラウンドスレッドで実行されているときに解決されないことを確認してください。バックグラウンドスレッドでインスタンスを解決するには、このインスタンスを「Per LifetimeScope」として登録してみてください:https ://simpleinjector.readthedocs.io/en/latest/lifetimes.html#scoped 。

...この例外はHttpContext.Current == null、私が知る限り、SignalRがHubを呼び出すためにインスタンスを要求したときにのみ発生することに注意してくださいOnDisconnected()

Webリクエストごとの依存関係のソリューションHub

これらのどれも本当に理想的ではないことに注意してください、それはすべてあなたのアプリケーション要件に依存します。

Ninjectで

非一時的な依存関係が必要な場合はOnDisconnected()、クラスの依存関係をオーバーライドしたり、カスタムを実行したりしないでください。その場合、グラフ内の各依存関係は個別の(一時的な)インスタンスになります。

SimpleInjectorで

、、、またはのいずれかとの間のハイブリッドライフスタイルが必要です。がnullでない場合、依存関係は、通常予想されるWebリクエストの間だけ存続します。ただし、がnullの場合、依存関係は一時的に、シングルトンとして、またはライフタイムスコープ内に注入されます。WebRequestLifestlyeLifestyle.TransientLifestyle.SingletonLifetimeScopeLifestyleHttpContext.CurrentHttpContext.Current

var lifestyle = Lifestyle.CreateHybrid(
    lifestyleSelector: () => HttpContext.Current != null,
    trueLifestyle: new WebRequestLifestyle(),
    falseLifestyle: Lifestyle.Transient // this is what ninject does
    //falseLifestyle: Lifestyle.Singleton
    //falseLifestyle: new LifetimeScopeLifestyle()
);

詳細についてLifetimeScopeLifestyle

私の場合、EntityFrameworkのDbContext依存関係があります。これらは、一時的にまたはシングルトンとして登録されたときに問題を引き起こす可能性があるため、注意が必要な場合があります。一時的に登録すると、2つ以上のDbContextインスタンスに接続されているエンティティを操作しようとしているときに、例外が発生する可能性があります。シングルトンとして登録すると、より一般的な例外が発生します(シングルトンとして登録しないでくださいDbContext)。私の場合DbContext、同じインスタンスを多くのネストされた操作で再利用できる特定のライフタイム内に存在する必要がありました。つまり、が必要でしたLifetimeScopeLifestyle

上記のハイブリッドコードをこのfalseLifestyle: new LifetimeScopeLifestyle()行で使用した場合、カスタムIHubActivator.Createメソッドの実行時に別の例外が発生します。

タイプNameOfYourHubの登録済みデリゲートが例外をスローしました。NameOfYourLifetimeScopeDependencyは「LifetimeScope」として登録されていますが、インスタンスはライフタイムスコープのコンテキスト外で要求されています。必ず最初にcontainer.BeginLifetimeScope()を呼び出してください。

ライフタイムスコープの依存関係を設定する方法は次のようになります。

using (simpleInjectorContainer.BeginLifetimeScope())
{
    // resolve solve dependencies here
}

ライフタイムスコープに登録されている依存関係は、このusingブロック内で解決する必要があります。さらに、これらの依存関係のいずれかが実装されている場合、それらはブロックIDisposableの最後で破棄されます。using次のようなことをしたくはありません。

public IHub Create(HubDescriptor descriptor)
{
    if (HttpContext.Current == null)
        _container.BeginLifetimeScope();
    return _container.GetInstance(descriptor.HubType) as IHub;
}

私はスティーブン(あなたが知らなかった場合に備えてSimpleInjectorの作者でもある)にこれについて尋ねたところ、彼は次のように述べました。

ええと..LifetimeScopeを破棄しないと、大きな問題が発生するので、必ず破棄してください。スコープを破棄しないと、ASP.NETで永遠にぶらぶらします。これは、スコープをネストして親スコープを参照できるためです。したがって、スレッドは最も内側のスコープ(キャッシュを含む)を存続させ、このスコープは親スコープ(キャッシュを含む)を存続させます。ASP.NETはスレッドをプールし、プールからスレッドを取得するときにすべての値をリセットしないため、すべてのスコープが有効なままであり、次にプールからスレッドを取得して新しい有効期間スコープを開始するときに、単純に新しいネストされたスコープを作成すると、これは積み重なっていきます。遅かれ早かれ、OutOfMemoryExceptionが発生します。

IHubActivator依存関係のスコープを設定するために使用することはできません。これは、依存関係Hubが作成するインスタンスほど存続しないためです。BeginLifetimeScope()したがって、メソッドをブロックでラップした場合でも、依存関係はインスタンスが作成さusingれた直後に破棄されます。Hubここで本当に必要なのは、間接参照の別のレイヤーです。

スティーブンの助けのおかげで、私が最終的に得たのは、コマンドデコレータ(およびクエリデコレータ)です。AHubは、Web要求ごとのインスタンス自体に依存することはできませんが、代わりに、実装が要求ごとのインスタンスに依存する別のインターフェースに依存する必要があります。コンストラクターに注入される実装Hubは、ライフタイムスコープを開始して破棄するラッパーで(simpleinjectorを介して)装飾されます。

public class CommandLifetimeScopeDecorator<TCommand> : ICommandHandler<TCommand>
{
    private readonly Func<ICommandHandler<TCommand>> _handlerFactory;
    private readonly Container _container;

    public CommandLifetimeScopeDecorator(
        Func<ICommandHandler<TCommand>> handlerFactory, Container container)
    {
        _handlerFactory = handlerFactory;
        _container = container;
    }

    [DebuggerStepThrough]
    public void Handle(TCommand command)
    {
        using (_container.BeginLifetimeScope())
        {
            var handler = _handlerFactory(); // resolve scoped dependencies
            handler.Handle(command);
        }
    }
}

ICommandHandler<T>... Webリクエストごとのインスタンスに依存するのは装飾されたインスタンスです。使用されるパターンの詳細については、これこれをお読みください。

登録例

container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>), assemblies);

container.RegisterSingleDecorator(
    typeof(ICommandHandler<>),
    typeof(CommandLifetimeScopeDecorator<>)
);
于 2013-08-28T12:23:05.237 に答える
6

更新この回答はSignalRバージョン1.0用に更新されました

IDependencyResolverSimpleInjector用のSignalRを作成する方法は次のとおりです。

public sealed class SimpleInjectorResolver 
    : Microsoft.AspNet.SignalR.IDependencyResolver
{
    private Container container;
    private IServiceProvider provider;
    private DefaultDependencyResolver defaultResolver;

    public SimpleInjectorResolver(Container container)
    {
        this.container = container;
        this.provider = container;
        this.defaultResolver = new DefaultDependencyResolver();
    }

    [DebuggerStepThrough]
    public object GetService(Type serviceType)
    {
        // Force the creation of hub implementation to go
        // through Simple Injector without failing silently.
        if (!serviceType.IsAbstract && typeof(IHub).IsAssignableFrom(serviceType))
        {
            return this.container.GetInstance(serviceType);
        }

        return this.provider.GetService(serviceType) ?? 
            this.defaultResolver.GetService(serviceType);
    }

    [DebuggerStepThrough]
    public IEnumerable<object> GetServices(Type serviceType)
    {
        return this.container.GetAllInstances(serviceType);
    }

    public void Register(Type serviceType, IEnumerable<Func<object>> activators)
    {
        throw new NotSupportedException();
    }

    public void Register(Type serviceType, Func<object> activator)
    {
        throw new NotSupportedException();
    }

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

残念ながら、のデザインに問題がありDefaultDependencyResolverます。そのため、上記の実装はそれを継承せず、ラップします。SignalRサイトでこれに関する問題を作成しました。あなたはここでそれについて読むことができます。設計者は私に同意しましたが、残念ながらこの問題はバージョン1.0では修正されていません。

これがお役に立てば幸いです。

于 2012-05-11T21:56:18.320 に答える
4

SignalR 2.0(およびベータ版)から、依存関係リゾルバーを設定する新しい方法があります。SignalRは、構成を行うためにOWINスタートアップに移動しました。Simple Injectorを使用すると、次のようになります。

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var config = new HubConfiguration()
        {
            Resolver = new SignalRSimpleInjectorDependencyResolver(Container)
        };
        app.MapSignalR(config);
    }
}

public class SignalRSimpleInjectorDependencyResolver : DefaultDependencyResolver
{
    private readonly Container _container;
    public SignalRSimpleInjectorDependencyResolver(Container container)
    {
        _container = container;
    }
    public override object GetService(Type serviceType)
    {
        return ((IServiceProvider)_container).GetService(serviceType)
               ?? base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.GetAllInstances(serviceType)
            .Concat(base.GetServices(serviceType));
    }
}

次のようにハブを明示的に注入する必要があります。

container.Register<MessageHub>(() => new MessageHub(new EFUnitOfWork()));

この構成は、トラフィックの多いWebサイトで問題なくライブで実行されています。

于 2013-08-30T15:58:53.703 に答える
0

以下は私のために働いた。さらに、依存関係リゾルバーをインスタンス化する前に、ハブクラスのコンテナーにデリゲートを登録する必要があります。

ex: container.Register<MyHub>(() =>
        {
            IMyInterface dependency = container.GetInstance<IMyInterface>();

            return new MyHub(dependency);
        });

public class SignalRDependencyResolver : DefaultDependencyResolver
{
    private Container _container;
    private HashSet<Type> _types = new HashSet<Type>();

    public SignalRDependencyResolver(Container container)
    {
        _container = container;

        RegisterContainerTypes(_container);
    }

    private void RegisterContainerTypes(Container container)
    {
        InstanceProducer[] producers = container.GetCurrentRegistrations();

        foreach (InstanceProducer producer in producers)
        {
            if (producer.ServiceType.IsAbstract || producer.ServiceType.IsInterface)
                continue;

            if (!_types.Contains(producer.ServiceType))
            {
                _types.Add(producer.ServiceType);
            }
        }
    }

    public override object GetService(Type serviceType)
    {
        return _types.Contains(serviceType) ? _container.GetInstance(serviceType) : base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return _types.Contains(serviceType) ? _container.GetAllInstances(serviceType) : base.GetServices(serviceType);
    }
}
于 2013-03-29T17:32:33.647 に答える
0

.NET Core 3.xsignalRでは、Transientになりました。DIを使用してハブを注入できます。したがって、ハブのマッピングを開始し、インターフェイスとハブを実装し、DIを介してそのコンテキストにアクセスします。

起動:

app.UseSignalR(routes =>
{
    routes.MapHub<YourHub>(NotificationsRoute); // defined as string
});
services.AddSignalR(hubOptions =>
{
    // your options
})

次に、次のようなインターフェイスを実装します。

public interface IYourHub
{
    // Your interface implementation
}

そしてあなたのハブ:

public class YourHub : Hub<IYourHub>
{
    // your hub implementation
}

最後に、次のようにハブを注入します。

private IHubContext<YourHub, IYourHub> YourHub
{
    get
    {
        return this.serviceProvider.GetRequiredService<IHubContext<YourHub, IYourHub>>();
    }
}

また、ハブ(ミドルウェア)のサービスを定義し、コンテキストをクラスに直接注入しないようにすることもできます。

インターフェイスでメソッドを定義したと想像Messageしてください。そうすれば、クラスで次のようなメッセージを送信できます。

await this.YourHub.Clients.Group("someGroup").Message("Some Message").ConfigureAwait(false);

インターフェイスに実装されていない場合は、次を使用します。

await this.YourHub.Clients.Group("someGroup").SendAsync("Method Name", "Some Message").ConfigureAwait(false);
于 2020-02-17T11:46:28.533 に答える