2

nhibernate プロファイラーを使用してサイトをスローすると、このメッセージが表示されました

警告: 暗黙的なトランザクションの使用はお勧めできません

http://nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions

それらはすべてのselectステートメントにあることがわかります。

private readonly ISession session;

public OrderHistoryRepo(ISession session)
{
    this.session = session;
}

public void Save(OrderHistory orderHistory)
{
    session.Save(orderHistory);
}

public List<OrderHistory> GetOrderHistory(Guid Id)
{
    List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
    return orderHistories;
}

public void Commit()
{
    using (ITransaction transaction = session.BeginTransaction())
    {
        transaction.Commit();
    }
}

コミットと同じように、トランザクションで GetOrderHistory をラップする必要がありますか?

編集

selectステートメントをトランザクションでどのようにラップしますか? こんな感じでしょうか?ただし、「トランザクション」は使用されません。

    public List<OrderHistory> GetOrderHistory(Guid Id)
    {
        using (ITransaction transaction = session.BeginTransaction())
        {       

 List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
        return orderHistories;
        }
    }

編集

Ninject (セッションを取得したときのように、それを活用して私を助けることができるかもしれません)

public class NhibernateSessionFactory
    {
        public ISessionFactory GetSessionFactory()
        {
           ISessionFactory fluentConfiguration = Fluently.Configure()
                                                  .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")))
                                                  .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Map>().Conventions.Add(ForeignKey.EndsWith("Id")))
                                                  .ExposeConfiguration(cfg => cfg.SetProperty("adonet.batch_size", "20"))
                                                  //.ExposeConfiguration(BuidSchema)
                                                  .BuildSessionFactory();

            return fluentConfiguration;
        }

        private static void BuidSchema(NHibernate.Cfg.Configuration config)
        {
            new NHibernate.Tool.hbm2ddl.SchemaExport(config).Create(false, true);
        }
    }


public class NhibernateSessionFactoryProvider : Provider<ISessionFactory>
    {   
        protected override ISessionFactory CreateInstance(IContext context)
        {
            var sessionFactory = new NhibernateSessionFactory();
            return sessionFactory.GetSessionFactory();
        }
    }

  public class NhibernateModule : NinjectModule
    {
        public override void Load()
        {
            Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope();
            Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
        }
    }

編集 3

私がこれをしたら

    public List<OrderHistory> GetOrderHistory(Guid Id)
    {
        using (ITransaction transaction = session.BeginTransaction())
        {       

 List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList();
        return orderHistories;
        }
    }

このアラートが表示されます

私がこれをしたら

    public List<OrderHistory> GetOrderHistory(Guid Id)
    {
        using (ITransaction transaction = session.BeginTransaction())
        {       

 List<OrderHistory> orderHistories = session.Query<OrderHistory>().Where(x => x.Id == Id).ToList().ConvertToLocalTime(timezoneId);
        transaction.Commit();
        return orderHistories;
        }
    }

エラーを取り除くことはできますが、予期しない結果が生じる可能性があります。

たとえば、orderHistories を取得すると、それらすべてをループして、「購入日」をユーザーの現地時間に変換します。これは、リスト用に作成した拡張メソッドを介して行われます。

変換したら、オブジェクトの「購入日」を上書きするように設定しました。この方法では、フィールドを 1 回変更するたびに新しいオブジェクトを作成する必要がありません。

コミットを呼び出す前にこの日付の変換を行うと、nhibernate はオブジェクトを更新したと判断し、コミットする必要があります。

だから私はこの質問に賞金をかけています。

  1. 各メソッドをトランザクションでラップする必要がないように、メソッドを作成するにはどうすればよいですか? 私はすでにセッションにninjectを使用しているので、それを活用できるかもしれませんが、1回のリクエストで複数のトランザクションを実行する必要があります。

したがって、リクエストごとに1つのトランザクションしかないことがソウルションであることはわかりません。

  1. 一時的な使用のために変更しているオブジェクトが誤ってコミットされないようにするにはどうすればよいですか?

  2. サービスレイヤーで使用している遅延読み込みを行うにはどうすればよいですか。遅延読み込みは通常サービス層で使用されるため、トランザクションで囲みたくありません。

リポジトリ パターンを使用している場合、それを行う方法の例を見つけるのは非常に難しいと思います。例では、すべてが常に同じトランザクションで記述されており、サービス層にトランザクションを持ちたくありません (ビジネス ロジックではなくレポの仕事です)。

4

4 に答える 4

4

NHibernate コミュニティは、実行する内容に関係なく、すべてをトランザクションでラップすることを推奨しています。

2 番目の質問に答えるには、一般的に状況によって異なります。これが Web アプリケーションの場合は、セッションごとのリクエスト パターンを確認する必要があります。最も基本的なシナリオでは、これが意味することは、HTTP 要求ごとに 1 つのセッションを作成し、要求が行われたときにセッション (およびトランザクション) が作成され、要求の最後にコミット/破棄されることです。これがあなたにとって正しい方法だと言っているわけではありませんが、ほとんどの人にとってうまくいく一般的なアプローチです。

これがどのように行われるかを示す多くの例があります。時間をかけて検索し、物事を読む価値は間違いなくあります。


編集:リクエストごとにセッション/トランザクションを行う方法の例:

依存関係リゾルバーからセッションをロードする SessionModule があります (これは MVC3 機能です)。

namespace My.Web
{
    public class SessionModule : IHttpModule {
        public void Init(HttpApplication context) {
            context.BeginRequest += context_BeginRequest;
            context.EndRequest += context_EndRequest;
        }

        void context_BeginRequest(object sender, EventArgs e) {
            var session = DependencyResolver.Current.GetService<ISession>();
            session.Transaction.Begin();
        }

        void context_EndRequest(object sender, EventArgs e) {
            var session = DependencyResolver.Current.GetService<ISession>();
            session.Transaction.Commit();
            session.Dispose(); 
        }

        public void Dispose() {}
    }
}

これは、セッションを登録する方法です (StructureMap を使用):

new Container(x => {

    x.Scan(a => {
        a.AssembliesFromApplicationBaseDirectory();
        a.WithDefaultConventions();
    });

    x.For<ISessionFactory>().Singleton().Use(...);
    x.For<ISession>().HybridHttpOrThreadLocalScoped().Use(sf => sf.GetInstance<ISessionFactory>().OpenSession());
    x.For<StringArrayType>().Use<StringArrayType>();

});

これは私が実験したものであり、NHibernate を使用したシナリオでうまく機能することがわかっていることに注意してください。他の人は異なる意見を持っているかもしれません (もちろん、それは歓迎です)。

于 2011-05-24T00:03:49.497 に答える
2

ISession をリポジトリのコンストラクターに渡していますが、これは良いことです。しかし、それが私がこのクラスで好きな唯一のことです。

  • Save は session.Save を呼び出すだけなので、必要ありません。
  • GetOrderHistory は、ID によって単一のエンティティを取得しているように見えますsession.Get<OrderHistory>(id)。これを使用する必要があります。必要に応じて、結果をコレクションに入れることができます。
  • Commit メソッドはリポジトリにあるべきではありません。

質問に直接答えるには...

各メソッドをトランザクションでラップする必要がないように、メソッドを作成するにはどうすればよいですか? 私はすでにセッションにninjectを使用しているので、それを活用できるかもしれませんが、1回のリクエストで複数のトランザクションを実行する必要があります。

私がお勧めするパターンは以下です。これは手動の依存性注入を使用しますが、Ninject を使用して依存性を解決できます。

List<OrderHistory> orderHistories;
var session = GetSession(); // Gets the active session for the request
var repository = new OrderHistory(Repository);
// new up more repositories as needed, they will all participate in the same transaction
using (var txn = session.BeginTransaction())
{
    // use try..catch block if desired
    orderHistories = repository.GetOrderHistories();
    txn.Commit();
}

したがって、リクエストごとに1つのトランザクションしかないことがソウルションであることはわかりません。

1 つのセッションで複数のトランザクションを実行してもまったく問題ありません。ユーザーに良いフィードバックを提供するには遅すぎるため、リクエストがコミットされるまで待つのは好きではありません。

一時的な使用のために変更しているオブジェクトが誤ってコミットされないようにするにはどうすればよいですか?

確実な唯一の方法は、IStatelessSession を使用することです。あまり確実でない方法は、セッションからオブジェクトを削除するか、セッションをクリアすることです。ただし、NHibernate では、永続オブジェクトを変更することはお勧めしません。

サービスレイヤーで使用している遅延読み込みを行うにはどうすればよいですか。遅延読み込みは通常サービス層で使用されるため、トランザクションで囲みたくありません。

リクエストごとのセッションを使用している場合、これは問題になりません。しかし、トランザクションの外部で遅延読み込みが発生する可能性があることは間違いありません。これらの警告は無視します。遅延ロードがトランザクションに含まれるように、必要なすべての子オブジェクトに「触れる」ことができると思いますが、気にしません。

サービス層にトランザクションを持ちたくない (ビジネス ロジックではなくレポの仕事です)

私はこれに同意しません。UI またはビジネス ロジックでトランザクションを管理する必要があります。UI は、ユーザーが意図 (変更を保存またはキャンセル) を表明する場所であり、トランザクションを管理する自然な場所です。

于 2011-06-09T21:01:57.963 に答える
2

アプリケーションで実行する読み取りの種類に適したトランザクション レベルを設定できると思いますが、問題は、アプリケーション コード内でそれを行う必要があるかどうかです。(n)hibernate が構成によって適用されるデフォルトのトランザクション レベルとは異なるユース ケースがない限り、私の推測ではノーです。

nhibernate 設定でトランザクション レベルを設定できるかもしれません。

それとも、プロファイラーの設定が少し熱心すぎるのでしょうか? ここからはわかりません。

しかし: 読み取りトランザクションをコミットしようとしましたか? 害を及ぼすべきではありません。

于 2011-05-24T00:59:50.400 に答える
0
  1. 推奨されるアプローチは、リクエストごとの作業単位 (セッション + トランザクション) です。確かに、NInject を使用してセッションのライフサイクルを管理できます。私 は最近、Castle Windsor を使用した同様のアプローチについてブログを書きました。
  2. 以下に 4 つのオプションを示します。

    • エンティティを一時的に変更しないでください
    • このような場合はステートレス セッションを使用します
    • 一時的な変更を行う場合は、オブジェクトを切り離します
    • ロールバック トランザクション

    私は最初のもので行きます。

  3. セッションごとのリクエスト パターンを使用している場合は、遅延読み込みについて心配する必要はありません。同じリクエストで実行され、トランザクションで自動的にラップされます。
于 2011-06-09T19:53:01.490 に答える