0

モデルの子エンティティを解決しようとして一連の問題が発生しています。永続化には nhibernate、ioc には windsor、マッピングには automapper を使用しています。

私はこれをいくつかの方法で攻撃しましたが、ほとんどの場合、途中でブロックされます。どんな助けも大歓迎です。

以下のコードの私の問題は、次の方法でページレイアウトを更新しようとしたときです。(layout-id のみが変更されていると仮定します)

        var page = _pageRepository.Get(model.Id);
        Mapper.Map(model, page);

        using (ITransaction tran = _sessionFactory.BeginTransaction())
        {
            _pageRepository.Update(page);
            tran.Commit();
        }

次のような素晴らしいエラーが表示されます。

同じ識別子の値を持つ別のオブジェクトが、レイアウト モデルのセッションに既に関連付けられていました。

今私は試しました: - 施設を perwebrequest に変更します (その後、セッションが閉じられていると言います) - 取得後にキャッシュからレイアウトを削除しようとしました (上記のエラー) - リゾルバーで既存のセッションを取得しようとしました (コンテキスト エラー)

これにさらにどのようにアプローチすればよいですか?きっとこんなに大変じゃない!どこが間違っていますか?本当にありがとう。

ここにすべての重要なビットがあります。

私はこのようなモデルを持っています:

public class ContentPage : Page
{ 
    public virtual Layout Layout { get; set; } 
}

次のように永続的な機能を使用して、nhibernate セッションを管理します。

        Kernel.Register(
            Component.For<ISessionFactory>()
                .UsingFactoryMethod(_ => config.BuildSessionFactory()),

            Component.For<ISession>()
                .UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession())
                .LifestylePerThread() <-- IMPORTANT FOR LATER.
            );

そして、私のマッピングは次のようになります:

        CreateMap<BlaViewModel, ContentPage>()
            .ForMember(dest => dest.DateModified, src => src.MapFrom(x => DateTime.UtcNow)) 
            .ForMember(x => x.Layout, x => x.ResolveUsing<EntityResolver<Layout>>().FromMember(y => y.Layout_Id));

そして最後に、私のリゾルバーは次のようになります:

public class EntityResolver<T> : ValueResolver<Guid, T> where T : EntityBase
{
    private readonly ISession _session;

    public EntityResolver(ISession session)
    {
        _session = session;
    }

    protected override T ResolveCore(Guid id)
    {
        var entity = _session.Get<T>(id); 
        return entity;
    }
}
4

3 に答える 3

1

例外は主に、セッションに既存の Id を持つ新しいオブジェクトを作成することから発生します。あなたの場合、AutoMapper がこれを行う可能性があります。

ContentPage マップで Layout プロパティをどのように構成しますか? デフォルトで使用すると、AutoMapper は新しい Layout オブジェクトを作成し、ID をそれに設定します。これは Session からロードされません。次に、このオブジェクトを保存すると、例外が発生する可能性があります。

そのため、レイアウト プロパティ マッピング ルールをカスタマイズし、既存のモデルの場合はセッション (リポジトリ) から取得し、その値を (AutoMapper または手動で) 設定する必要があります。セッションの状態は正しいです。

ContentPage の AutoMapper 構成は次のようになります。

Mapper.CreateMap<VPage, ContentPage>()
    ....
    .ForMember(des=>des.Layout, opt=>opt.MapFrom(src=>GetLayout(src))) //customize Layout
    ....;

GetLayout 関数では次のようになります。

private Layout GetLayout(VPage page)
{
    var layout = page.LayoutId == 0? new Layout() : _layoutRepository.Get(page.LayoutId); //avoid new Layout object with existed Id
    .......

    return layout;
}

さらに、AutoMapper を使用してドメイン モデルを DTO から変換しない方がよいでしょう。この説明を参照してください。

更新: EntityResolver が表示されず申し訳ありません。代わりに LayoutRepository を使用して取得してみてください。

于 2012-06-21T07:19:18.190 に答える
0

典型的なやり方で、これを数時間後に理解し、IOCに関するいくつかの難しい教訓を学びました。

上記のコードでは、次のように解決するISessionを登録したことがわかります。

 Kernel.Register(
        Component.For<ISessionFactory>()
            .UsingFactoryMethod(_ => config.BuildSessionFactory()),

        Component.For<ISession>()
            .UsingFactoryMethod(k => k.Resolve<ISessionFactory>().OpenSession())
            .LifestylePerThread() <-- IMPORTANT FOR LATER.
        );

これが私の問題の始まりでした。これは基本的にスレッドごとであったため、ほとんどすべての異なるポイントで新しいセッションが解決されました。セッションの複数のインスタンスで終了します。(したがって、エラー)

これを変更すると、.LifestylePerWebRequest()状況は少し良くなりましたが、それでもセッションの問題が発生していました。

最終的に、このセッションがすべてのレイヤーを通過していたことを理解し(IOCはコンストラクターを介して)、my Managerレイヤー、リポジトリレイヤー、およびそれが使用されていたすべての場所を、PerWebRequestとしてインストールするように変更する必要があります。

好き:

container.Register(Classes.FromAssemblyContaining<Repository>()
                               .Where(Component.IsInSameNamespaceAs<Repository>())
                               .WithService
                               .DefaultInterfaces()
                               .LifestylePerWebRequest());

と:

        container.Register(Component.For<EntityResolver<Layout>>().ImplementedBy<EntityResolver<Layout>>().LifestylePerWebRequest());

IOCを適切に使用すると、最終的に1つのセッションのみがスプールされ(PerWebRequest)、問題が解決するようになりました。

良い。同じ問題を見ている他の人に役立つことを願っています。

于 2012-06-21T22:23:28.950 に答える
0

私の推測では、リゾルバーは別のセッションを使用してレイアウトを取得しています。

// sample code
var layout1 = Resolve(1);

session.Attach(layout1);  // now contains layout 1

var layout2 = Resolve(1);

session.Attach(layout2);  // error: already contains layout with id 1


public Layout Resolve(int id)
{
    using (var session = OpenSession())
    {
        return GetNewSession.Get<Layout>(1);
    }
}

同じセッションを使用して、接続されたエンティティを解決する

于 2012-06-21T06:42:51.390 に答える