こんにちは、nhibernate の一部のプロが、会話ごとの Nhibernate セッションまたは unhaddins 実装の実装例を教えてくれますか? または、その方法を説明してください。よろしくエンディス
1 に答える
私は NHibernate を 4 年間使用しています。以前は、「open-session-per-operation」アンチパターンを使用していました。オブジェクトは常に切り離されていました。したがって、永続化するには、それらを再アタッチするか、それらの値をアタッチされたオブジェクトにコピーする必要がありました。これにより、多くのコード行と多くの「遅延初期化例外」が発生します。
最近、私は「会話パターン」を研究し、 「Spring.Net」インフラストラクチャ上で実装を行いました。この実装は、Issue SPRNET-1431 (「会話スコープ」および「セッションごとのセッション」の回避策)で「jira.springsource」に提出されました。
「サンプルアプリ」は作っていませんが、興味があれば作れます。
ハイルトン
補足回答:
サンプルアプリを用意し、SPRNET-1431 Workaround for 'conversation scope' and "session-per-conversation" にファイル "Spring.Conversation.example.7z" として投稿しました。
以下に、私が何をしたかを明確にする(またはしない)ための説明を書きました。
この「サンプルアプリ」は、「Spring.NET」のバージョン「1.3.0」に含まれる「Spring.Data.NHibernate.Northwind」をConversationを利用するために改変したものです。現在、「Spring.Net」には「会話スコープ」がなく、「拡張永続コンテキスト」(「会話戦略ごとのセッション」) の概念も実装されていません。
このサンプル アプリケーションの目的は次のとおりです。
- オブジェクトのインスタンスを「会話範囲」の模倣に保持する方法。に示されてい
expression="@(convCustomer)['CustomerEditController']"
ます。 - 「拡張永続コンテキスト」の楽しみ方。「遅延読み込みエラー」は発生しなくなり
ISession.Get
、「同じレコード」への呼び出しが繰り返されても、データベースへの多数のアクセスが発生せず、NHibernate キャッシュがより効率的に使用されます。"CustomerList.aspx" の変更は、これを示しています。会話の有効性を確認するには、「App_Code\ConversationPage.cs」行にコメントを付けますthis.Conversation.StartResumeConversation();
。「CustomerList.aspx」の「+」ボタンをクリックすると、「ロールのコレクションを遅延初期化できませんでした」というエラーが表示されます。 .
重要: アプリケーション全体で 1 つの会話を使用しないでください (「HTTP セッション」の期間は同じです)。NHibernate は、ロードされたすべてのオブジェクトのキャッシュを保持します。会話が長時間保持されると、このキャッシュは無限に大きくなる傾向があります (制限はデータベース レコードの量です)。つまり、各会話はアプリケーション ページのサブセットに制限する必要があり、このサブセットとの対話の最後に破棄する必要があります ( IConversationState.EndConversation()
)。推奨事項: <property name="EndPaused" value="true"/>
「Spring.Conversation.Imple.WebConversationManager」に保持して、会話を開始すると、他の会話は破棄されます。
追加情報: 単体テスト ("Spring.Northwind.IntegrationTests.2008") が機能していません。ただし、会話をサポートするための変更とは関係ないため、問題はありません。実際には、それ以前にも既にエラーが発生していました。
「Spring.Data.NHibernate.Northwind」の変更点のリスト:
- Spring.Northwind.Web.References.2008
- スキームへのリンクを追加してオートコンプリートを有効にします。
web.config
モジュール、追加:
<add name="ConversationModule" type="Spring.Conversation.HttpModule.ConversationModule, Spring.Conversation"/> <add name="ConversationModule" type="Spring.Conversation.HttpModule.ConversationModule, Spring.Conversation"/>
モジュール、削除:
<add name="OpenSessionInView" type="Spring.Data.NHibernate.Support.OpenSessionInViewModule, Spring.Data.NHibernate21"/>
web.xml
モジュール構成
<!--Configuration for Spring HttpModule interceptor's--> <object name="HttpApplicationConfigurer" type="Spring.Context.Support.HttpApplicationConfigurer, Spring.Web"> <property name="ModuleTemplates"> <dictionary> <entry key="ConversationModule"> <!-- this name must match the module name --> <object> <!-- select "view source" in your browser on any page to see the appended html comment --> <property name="ConversationManagerNameList"> <list element-type="string"> <value>conversationManager</value> </list> </property> </object> </entry> </dictionary> </property> </object>
会話マネージャー
<!--Conversation Manager--> <object name="conversationManager" type="Spring.Conversation.Imple.WebConversationManager, Spring.Conversation" scope="session"> <property name="SessionFactory" ref="NHibernateSessionFactory"/> <property name="EndPaused" value="true"/> </object>
お客様との会話
<!-- Conversation for 'CustomerEditor.aspx', 'CustomerList.aspx', 'CustomerOrders.aspx', 'CustomerView.aspx', and 'FulfillmentResult.aspx' --> <!-- Important: If the application had other parties ("management employees" for example), they should use another conversation. --> <object name="convCustomer" type="Spring.Conversation.Imple.WebConversationSpringState, Spring.Conversation" scope="session"> <property name="Id" value="convCustomer"></property> <property name="TimeOut" value="0"></property> <property name="ConversationManager" ref="conversationManager"></property> <property name="SessionFactory" ref="NHibernateSessionFactory"/> <property name="DbProvider" ref="DbProvider"/> <!-- Using workaround for 'conversation scope' to reference for 'CustomerEditController'. It is not as volatile as "request scope" not as durable as the "session scope" --> <property name="['CustomerEditController']" ref="CustomerEditController"></property> </object>
「CustomerEditController」スコープを変更し、[scope="session"] を削除して [singleton="false"] を配置します。
<object name="CustomerEditController" type="NHibernateCustomerEditController" singleton="false"> <constructor-arg name="sessionFactory" ref="NHibernateSessionFactory"/> </object> ...
"CustomerEditController"
、removeref="CustomerEditController"
および putの参照を変更しますexpression="@(convCustomer)['CustomerEditController']"
(「会話範囲」をシミュレートします):<!-- Using workaround for 'conversation scope' to reference for 'CustomerEditController'. It is not as volatile as "request scope" not as durable as the "session scope" --> <object name="CustomerEditPage" abstract="true"> <property name="CustomerEditController" expression="@(convCustomer)['CustomerEditController']"/> <property name="Conversation" ref="convCustomer"/> </object>
<!--
Using workaround for 'conversation scope' to reference for
'CustomerEditController'. It is not as volatile as "request scope"
not as durable as the "session scope"
-->
<object type="CustomerView.aspx">
<property name="CustomerDao" ref="CustomerDao" />
<property
name="CustomerEditController"
expression="@(convCustomer)['CustomerEditController']" />
<property name="Conversation" ref="convCustomer"/>
<property name="Results">
<dictionary>
<entry key="EditCustomer" value="redirect:CustomerEditor.aspx" />
<entry key="CustomerList" value="redirect:CustomerList.aspx" />
</dictionary>
</property>
</object>
<!--
Using workaround for 'conversation scope' to reference for
'CustomerEditController'. It is not as volatile as "request scope"
not as durable as the "session scope"
-->
<object type="FulfillmentResult.aspx">
<property name="FulfillmentService" ref="FulfillmentService" />
<property
name="CustomerEditController"
expression="@(convCustomer)['CustomerEditController']" />
<property name="Conversation" ref="convCustomer"/>
<property name="Results">
<dictionary>
<entry key="Back" value="redirect:CustomerOrders.aspx" />
</dictionary>
</property>
</object>
<object type="Default.aspx">
<property name="Conversation" ref="convDefault"/>
<property name="Results">
<dictionary>
<entry key="CustomerList" value="redirect:CustomerList.aspx" />
</dictionary>
</property>
</object>
<!--Conversation for 'Default.aspx'-->
<!--
"convDefault" will have only one functionality: trigger the release
of other conversations when started (StartResumeConversation())
-->
<object
name="convDefault"
type="Spring.Conversation.Imple.WebConversationSpringState, Spring.Conversation"
scope="session">
<property name="Id" value="convDefault"></property>
<property name="TimeOut" value="0"></property>
<property name="ConversationManager" ref="conversationManager"></property>
<property name="SessionFactory" ref="NHibernateSessionFactory"/>
<property name="DbProvider" ref="DbProvider"/>
</object>
- 「ConversationPage.cs」を追加しました。会話をサポートするベースページ。
- CustomerList.aspx
- 「遅延初期化エラー」なしで、同じページの「注文」をリストに許可します。すべてのオブジェクトは ISession (NHibernate) に接続されたままです。
- CustomerList.aspx.cs:
- プロパティを追加しました
IList<Customer> CustomersLoadedOncePerConvList
。リストは 1 回だけ読み込まれ、会話ごとに 1 回だけデータベースを検索します。 - を変更し
Page_InitializeControls
て検討しCustomersLoadedOncePerConvList
ます。 - このメソッド
BtnShowOrders_Click
は、 に対して暗黙的に「遅延ロード」を実行しCustomer.Orders
ます。
- プロパティを追加しました
??? : Spring.Web.UI.Page
オンに変更??? : Spring.Web.UI.Page
:- CustomerEditor.aspx.cs
- CustomerList.aspx.cs
- CustomerOrders.aspx.cs
- CustomerView.aspx.cs
- FullfillmentResult.aspx.cs
- Default.aspx.cs
Dao.xml
<entry key="connection.release_mode" value="on_close"/>
各 IDbCommand 実行前後の切断と再接続を回避するために追加されました。トランザクションの境界外で多数の遅延ロードが発生する可能性があるため、これは重要です。追加した:
... <entry key="show_sql" value="true"/> <entry key="format_sql" value="true"/> ...
「Default.aspx.cs」から削除します (使用されません)。
customerDao
;fulfillmentService
;CustomerDao
;Button1_Click(object sender, EventArgs e)
;ProcessCustomer()
;
Config\Log4Net.xml。
... <!--detail's about SQL's. To view sql commands on Logs\log.txt--> <logger name="NHibernate.SQL"> <level value="DEBUG" /> </logger> ... <!--detail's about Conversation--> <logger name="Spring.Conversation"> <level value="DEBUG" /> </logger>