6

私のサービスレイヤーUnitOfWorkでは、コンストラクターにと2つのリポジトリーを挿入しました。作業ユニットとリポジトリにはDbContext、2人の間で共有したいインスタンスがあります。Ninjectでそれを行うにはどうすればよいですか?どのスコープを考慮する必要がありますか?

私はWebアプリケーションを使用していないため、を使用できませんInRequestScope

私は似たようなことをしようとしています...そして私はDIを使用していますが、UoWをDisposedにして、このように作成する必要があります。

using (IUnitOfWork uow = new UnitOfWorkFactory.Create())
{
    _testARepository.Insert(a);
    _testBRepository.Insert(b);

    uow.SaveChanges();
}

編集:私は理解していることを確認したいだけです... https://github.com/ninject/ninject.extensions.namedscope/wiki/InNamedScopeを見てから、実際にNinjectを使用している現在のコンソールアプリケーションアーキテクチャについて説明します。

まあ言ってみれば :

クラスAはサービスレイヤークラスです

クラスBは、パラメーターにインターフェース(IContextFactory)を取り込む作業単位です。

クラスCは、パラメーターにインターフェース(IContextFactory)を取り込むリポジトリーです。

ここでの考え方は、2つ以上のリポジトリでコンテキスト操作を実行し、作業単位を使用して変更を適用できるようにすることです。

クラスDは、クラスBとCの間で共有されるコンテキストのインスタンス(コンテナーに保持)を提供するコンテキストファクトリ(Entity Framework)です(..他のリポジトリーにも使用されます)。

コンテキストファクトリはインスタンスをコンテナに保持するため、サービス操作の最後にコンテキストを破棄する必要があるため、このインスタンスをすべての名前で再利用したくありません。これがInNamedScopeの主な目的ですか?

解決策は次のようになりますが、私はそれを正しく行っているかどうかはまったくわかりません。サービスインスタンスは一時的であり、実際には破棄されないということですか?:

Bind<IScsContextFactory>()
    .To<ScsContextFactory>()
    .InNamedScope("ServiceScope")
    .WithConstructorArgument(
         "connectionString", 
         ConfigurationUtility.GetConnectionString());

Bind<IUnitOfWork>().To<ScsUnitOfWork>();

Bind<IAccountRepository>().To<AccountRepository>();
Bind<IBlockedIpRepository>().To<BlockedIpRepository>();

Bind<IAccountService>().To<AccountService>().DefinesNamedScope("ServiceScope");
Bind<IBlockedIpService>().To<BlockedIpService>().DefinesNamedScope("ServiceScope");
4

2 に答える 2

5

InCallscope更新:このアプローチは現在のNuGetに対して機能しますが、現在の不安定なNuGetパッケージで修正された実装の異常に依存しています。いくつか検討した後の最善のアプローチを反映するために、数日以内にこの回答を微調整します。NB構造化の高レベルの方法はほとんど同じままであり、Bind<DbContext>()スコープの正確な詳細だけが機能します。(ヒント:CreateNamedScope不安定な状態では機能するか、コマンドハンドラーを次のように設定できDefinesNamedScopeます。私がそうしない理由は、うまく構成/再生できるものが欲しいということです InRequestScope


統合テストを読むことを強くお勧めしNinject.Extensions.NamedScopeます(真剣に、それらを見つけて読んで再読してください)

これDbContext 作業単位であるため、これ以上ラッピングする必要はありません。

複数の「リクエスト」を実行できるようにし、それらの間で単一の作業ユニットを共有したい場合は、次のことを行う必要があります。

Bind<DbContext>()
    .ToMethod( ctx => 
        new DbContext( 
            connectionStringName: ConfigurationUtility.GetConnectionString() ))
    .InCallScope();

InCallScope()つまり:

  1. 単一のkernel.Get() 呼び出し用に構成された特定のオブジェクトグラフ(したがって、呼び出しスコープ内)の場合、を必要とするすべての人がDbContext同じインスタンスを取得します。
  2. IDisposable。_ ルートオブジェクトに対してが発生したDispose()ときに呼び出されます(そうでない場合はルートに対して発生します)Kernel.Release()Kernel.Components.Get<ICache>().Clear().InCallScope()

InNamedScope()を使用する理由はないはずDefinesNamedScope()です; デフォルトのプーリング/子育て/グループ化から除外しようとしている長期的なオブジェクトはありません。

上記を行うと、次のことができるようになります。

var command = kernel.Get<ICommand>();
try {
    command.Execute();
} finally {
    kernel.Components.Get<ICache>().Clear( command ); // Dispose of DbContext happens here
}

コマンドの実装は次のようになります。

class Command : ICommand {
    readonly IAccountRepository _ar;
    readonly IBlockedIpRepository _br;
    readonly DbContext _ctx;
    public Command(IAccountRepository ar, IBlockedIpRepository br, DbContext ctx){
        _ar = ar;
        _br = br;
        _ctx = ctx;
    }
    void ICommand.Execute(){
        _ar.Insert(a);
        _br.Insert(b);
        _ctx.saveChanges();
    }
}

一般に、私はこのように暗黙の作業単位を持つことを避け、代わりにその作成とを表面化することに注意してくださいDisposal。これにより、コマンドは次のようになります。

class Command : ICommand {
    readonly IAccountService _as;
    readonly IBlockedIpService _bs;
    readonly Func<DbContext> _createContext;
    public Command(IAccountService @as, IBlockedIpServices bs, Func<DbContext> createContext){
        _as = @as;
        _bs = bs;
        _createContext = createContext;
    }
    void ICommand.Execute(){
        using(var ctx = _createContext()) {
            _ar.InsertA(ctx);
            _br.InsertB(ctx);
            ctx.saveChanges();
        }
   }

これには、の使用は含まれません.InCallScope()(ただし、単純なからを合成するには、Bind<DbContext>()存在が必要です。Ninject.Extensions.FactoryFactoryModuleFunc<DbContext>Bind<DbContext>()

于 2013-03-24T22:22:35.840 に答える
2

他の回答で説明されているように、InCallScopeはこの問題を解決するための良いアプローチではありません。

今のところ、明確な説明なしに、最新のNuGet Unstable / IncludePreRelease/ Instal-Package -Preエディションに対して機能するコードをダンプしています。Ninject.Web.Commonこれをウィキの記事に翻訳しNinject.Extensions.NamedScopeます。ある段階でNinject.Extensions.NamedScopeウィキのCreateNamedScope/GetScopeの記事にこの手法のウォークスルーを書き始めました。

おそらく、いくつかのビットは、ある段階でもプルリクエストになります(アウトラインコードを提供してくれた@Remo Gloorへのハットチップ)。関連するテストと学習テストは今のところこの要点にあります)、適切なリリース形式の未定でのパッケージ化が保留されています。

execの概要は、以下のモジュールをカーネルにロードし、ハンドラーの呼び出しごと.InRequestScope()に作成/ Disposedして必要なすべてのものを使用し、を介して要求をフィードすることIHandlerComposer.ComposeCallDisposeです。

次のモジュールを使用する場合:

public class Module : NinjectModule
{
    public override void Load()
    {
        Bind<IHandlerComposer>().To<NinjectRequestScopedHandlerComposer>();

        // Wire it up so InRequestScope will work for Handler scopes
        Bind<INinjectRequestHandlerScopeFactory>().To<NinjectRequestHandlerScopeFactory>();
        NinjectRequestHandlerScopeFactory.NinjectHttpApplicationPlugin.RegisterIn( Kernel );
    }
}

Factory [1]のどのワイヤで、次のNinjectHttpApplicationPluginことが公開されます。

public interface INinjectRequestHandlerScopeFactory
{
    NamedScope CreateRequestHandlerScope();
}

次に、このComposerを使用してリクエストを実行できますInRequestScope()

public interface IHandlerComposer
{
    void ComposeCallDispose( Type type, Action<object> callback );
}

実装:

class NinjectRequestScopedHandlerComposer : IHandlerComposer
{
    readonly INinjectRequestHandlerScopeFactory _requestHandlerScopeFactory;

    public NinjectRequestScopedHandlerComposer( INinjectRequestHandlerScopeFactory requestHandlerScopeFactory )
    {
        _requestHandlerScopeFactory = requestHandlerScopeFactory;
    }

    void IHandlerComposer.ComposeCallDispose( Type handlerType, Action<object> callback )
    {
        using ( var resolutionRoot = _requestHandlerScopeFactory.CreateRequestHandlerScope() )
            foreach ( object handler in resolutionRoot.GetAll( handlerType ) )
                callback( handler );
    }
}

Ninjectインフラストラクチャのもの:

class NinjectRequestHandlerScopeFactory : INinjectRequestHandlerScopeFactory
{
    internal const string ScopeName = "Handler";

    readonly IKernel _kernel;

    public NinjectRequestHandlerScopeFactory( IKernel kernel )
    {
        _kernel = kernel;
    }

    NamedScope INinjectRequestHandlerScopeFactory.CreateRequestHandlerScope()
    {
        return _kernel.CreateNamedScope( ScopeName );
    }

    /// <summary>
    /// When plugged in as a Ninject Kernel Component via <c>RegisterIn(IKernel)</c>, makes the Named Scope generated during IHandlerFactory.RunAndDispose available for use via the Ninject.Web.Common's <c>.InRequestScope()</c> Binding extension.
    /// </summary>
    public class NinjectHttpApplicationPlugin : NinjectComponent, INinjectHttpApplicationPlugin
    {
        readonly IKernel kernel;

        public static void RegisterIn( IKernel kernel )
        {
            kernel.Components.Add<INinjectHttpApplicationPlugin, NinjectHttpApplicationPlugin>();
        }

        public NinjectHttpApplicationPlugin( IKernel kernel )
        {
            this.kernel = kernel;
        }

        object INinjectHttpApplicationPlugin.GetRequestScope( IContext context )
        {
            // TODO PR for TrgGetScope
            try
            {
                return NamedScopeExtensionMethods.GetScope( context, ScopeName );
            }
            catch ( UnknownScopeException )
            {
                return null;
            }
        }

        void INinjectHttpApplicationPlugin.Start()
        {
        }

        void INinjectHttpApplicationPlugin.Stop()
        {
        }
    }
}
于 2013-04-05T14:22:45.010 に答える