4

Unityを使用して完全に機能しているプロジェクトが1つあります。代わりにSimpleInjectorを使用するように切り替えてみましたが、データベースに変更が保存されることはありません。登録されているコンポーネントの寿命に関係していると思います。Unityコンテナの登録は次のとおりです。

private IUnityContainer GetUnityContainer()
{
    IUnityContainer container = new UnityContainer()
        .RegisterType<IDatabaseFactory, DatabaseFactory>(
            new HttpContextLifetimeManager<IDatabaseFactory>())
    .RegisterType<IUnitOfWork, UnitOfWork>(
        new HttpContextLifetimeManager<IUnitOfWork>())
    .RegisterType<ICategoryRepository, CategoryRepository>(
        new HttpContextLifetimeManager<ICategoryRepository>())
    .RegisterType<ICategoryService, CategoryService>(
        new HttpContextLifetimeManager<ICategoryService>());

    return container;         
}

そして、これが新しいSimpleInjectorの登録です。

container.Register<IDatabaseFactory, DatabaseFactory>();
container.Register<IUnitOfWork, UnitOfWork>();
container.Register<ICategoryRepository, CategoryRepository>();
container.Register<ICategoryService, CategoryService>();

SimpleInjectorでどのように機能するのかわかりませんHttpContextLifetimeManager。MVCはユニティの例のクライアントですが、私はWPFプロジェクトとSimpleInjectorに変更しています。どんな提案でも大歓迎です。ありがとう。


@スティーブン。ご意見ありがとうございます。私のRepositoryBaseとUnitOfWorkは、コンストラクターにIDatabaseFactoryを挿入するので、使用する必要があることを発見しましたcontainer.RegisterSingle<IDatabaseFactory, DatabaseFactory>()。これにより、1つの問題が解決しました。私はまだ生涯に問題があります。消費するアプリはWPFですが、RegisterPerWebRequestはどのように機能しますか?

私のプロジェクトには、DataLayer >> BusinessLayer >> WcfService>>WPFフロントエンドがあります。Simple InjectorはWcfServiceプロジェクトに設定されており、ビジネスレイヤーにはそこにアイテムを登録するためのBoostrapperがあります。今のところ、私のWPFクライアントはGetAllCountries()を実行し、グリッドに表示します。名前を変更して更新しようとすると、「同じキーを持つオブジェクトがObjectStateManagerにすでに存在します。ObjectStateManagerは同じキーを持つ複数のオブジェクトを追跡できません。」というメッセージが表示されます。エラー。デバッグを行ったところ、WPFクライアントでGetCountriesサービスを呼び出した後、更新を試みると、すべての国がdbContext.ChangeTracker.Entries()を介してコンテキストに接続されていることがわかりました。この時点では、最初の作業単位の後にコンテキストが破棄されているはずなので、追跡されているエンティティはありません。

MVCアプリでは、RegisterPerWebRequestがそれを修正しますが、WPFに相当するものは何ですか?今から拡張機能をインストールして試してみますが、探している解決策ではないと感じています。助けてくれてありがとう。


わかった。私はもう少し掘り下げて、うまくいく解決策を見つけました。それが正しいかどうかはわかりません。とにかく、今、物事を登録するためのブートストラッパーがある私のBLLでは、次のように登録できます。

container.RegisterPerWcfOperation<IDatabaseFactory, DatabaseFactory>();
container.RegisterPerWcfOperation<IUnitOfWork, UnitOfWork>();
container.RegisterPerWcfOperation<ICountryRepository, CountryRepository>();

それは私が探していたものを私に与えます。DatabaseFactoryのインスタンスは1つしか作成されないため、私のリポジトリと作業単位は、必要に応じてそれを共有します。また、クライアントでGetCountries()を実行した後、サービスを2回呼び出して実行および更新するときに、dbContext.ChangeTracker.Entries()を確認し、追跡されているエンティティがないことを確認します。これは正しいことです。これで、重複キーエラーが発生することなく、アタッチ、変更の設定、およびSaveChangesの呼び出しを行うことができます。これは大丈夫ですか?ありがとう。

4

1 に答える 1

6

一時的なライフスタイルを使用したSimpleInjectorレジスタタイプのRegisterオーバーロード(つまり、キャッシングなし)。インスタンスをリクエストする(またはインスタンスを挿入する)たびに、新しいインスタンスが作成されます。Unityでもこれは同じです。デフォルトのライフスタイルは一時的です。

これらのタイプをWebリクエストごとのライフスタイルに登録することは非常に重要なようです。IUnitOfWorkでそれらのコミットを行うクラスが、実際にに変更を加えるクラスとは異なるインスタンスを取得するときに、変更がデータベースにコミットされないことは不思議ではありませんIUnitOfWork

Unityに相当するSimpleInjectorHttpContextLifetimeManagerWebRequestLifestyleです。このライフスタイルはコアライブラリの一部ではありませんが、NuGetパッケージとして利用できます。

これをプロジェクトに含めた後、次の登録を行うことができます。

container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();

container.Register<IDatabaseFactory, DatabaseFactory>(Lifestyle.Scoped);
container.Register<IUnitOfWork, UnitOfWork>(Lifestyle.Scoped);
container.Register<ICategoryRepository, CategoryRepository>(Lifestyle.Scoped);
container.Register<ICategoryService, CategoryService>(Lifestyle.Scoped);

または同等のもの:

Lifestyle lifestyle = new WebRequestLifestyle();

container.Register<IDatabaseFactory, DatabaseFactory>(lifestyle);
container.Register<IUnitOfWork, UnitOfWork>(lifestyle);
container.Register<ICategoryRepository, CategoryRepository>(lifestyle);
container.Register<ICategoryService, CategoryService>(lifestyle);

のデフォルトの動作はWebRequestLifestyle、Webリクエストが終了したときに作成されたインスタンスを破棄することです。このための特別な登録は必要ありません(Simple InjectorはHttpModule、アプリケーションの起動時にあなたに代わって接続します)。


アップデート:

最後の行まで質問を読まなかったことをお詫びします。WPFクライアントを使用して構成する必要があるという事実を見逃しました。

ご存知かもしれませんが、クライアントはWCFサービスとは異なるアプリケーションであるため、システムには2つのコンポジションルートがあります。1つはクライアント用、もう1つはサービス用です。彼らの登録はおそらくかなり異なるでしょう。Simple Injector v4.1以降、WCFサービスの場合、実際にAsyncScopedLifestyleが必要になります。または、 dotnetjunkie / solidservicesの参照アーキテクチャに従うと、ThreadScopedLifestyleを使用して、2つのExecuteメソッドでスコープを明示的に定義するのが簡単になります。 。

特定のスコープの作業単位を定義するのは難しいため、2層アプリケーション(クライアント->データベース)のクライアントでオブジェクトの存続期間を管理するのはかなり難しいと思います。コマンド/ハンドラー+クエリ/ハンドラーのアプローチを使用しているので、物事はとても簡単になります。クライアントには作業単位はありません。サーバー上だけです。プレゼンターは、いくつかIQueryHandler<TQuery, TResult>ICommandHandler<TCommand>インターフェイスに依存するだけで済みます。クエリは状態を変更せず、コマンドは不可分操作である必要があります。つまり、作業単位は実行中のコマンドの境界内でのみ必要です。

于 2013-01-31T18:29:44.797 に答える