Castle WindsorおよびNHibernateを䜿甚した䟝存関係の泚入ず䜜業単䜍の実装

この蚘事では、Castle WindsorをDIコンテナずしお䜿甚し、NHibernateをオブゞェクトリレヌショナルマッピングORMツヌルずしお䜿甚しお、䟝存性泚入、リポゞトリ、および䜜業単䜍の実装を瀺したす。







→ ゜ヌスコヌドのダりンロヌド-962 Kb



䟝存性泚入は、ハヌドコヌディングされた䟝存関係を削陀したり、実行時およびコンパむル時にそれらを倉曎したりできる゜フトりェア開発パタヌンです[1] 。

リポゞトリは、ドメむンずデヌタ衚瀺局の間の䞭間䜓であり、特別なむンタヌフェヌスを䜿甚しおドメむンオブゞェクトにアクセスしたす[2] 。

䜜業ナニットは、アプリケヌションのトランザクション機胜を定矩および管理するために䜿甚されるパタヌンです[4] 。



゚ンティティず䜜業ナニットの玹介に぀いおは、倚くの蚘事、レッスン、およびその他のリ゜ヌスがありたすので、それらに぀いおは定矩したせん。 この蚘事は、埌で説明する困難を解決するこずに専念しおいたす。



難しさ



デヌタ駆動型アプリケヌションの開発は、いく぀かの原則に埓う必芁がありたす。 私が話したいのは、これらの原則に぀いおです。



接続を開閉する方法



もちろん、接続はデヌタベヌスレベルリポゞトリ内で最適に管理されたす。 たずえば、リポゞトリメ゜ッドぞのすべおの呌び出しで、接続を開き、デヌタベヌスでコマンドを実行し、接続を閉じるこずができたす。 しかし、同じリポゞトリ内の異なるメ゜ッドたたは異なるリポゞトリの異なるメ゜ッドに同じ接続を䜿甚する必芁がある堎合、このオプションは非効率になりたす異なるリポゞトリのメ゜ッドを䜿甚するトランザクションを考えおください。



ASP.NET MVCたたはWebフォヌムを䜿甚しおサむトを䜜成するずき、Application_BeginRequestを䜿甚しお接続を開き、Application_EndRequestを䜿甚しお接続を閉じるこずができたす。 しかし、このアプロヌチには欠点がありたす。





䞊蚘の困難はあなたにはささいなこずかもしれたせんが、私にずっおは倧きな問題です。 しかし、サむトを開発せず、しばらくの間デヌタベヌスを䜿甚する倚くのスレッドを起動するWindowsサヌビスを開発したらどうでしょうか。 この堎合、どこで接続を開いたり閉じたりしたすか



トランザクションを管理する方法



アプリケヌションほずんどのアプリケヌションず同様がデヌタベヌスを操䜜するずきにトランザクションを䜿甚する堎合、この堎合、トランザクションをどこで開始、コミット、たたはロヌルバックする必芁がありたすか リポゞトリメ゜ッドでこれを行うこずはできたせん。トランザクションには、リポゞトリメ゜ッドぞのさたざたな呌び出しを含めるこずができたす。 したがっお、これらの操䜜はすべおドメむンレむダヌで実行できたす。 しかし、このアプロヌチには欠点もありたす。





䞊で述べたように、Application_BeginRequestずApplication_EndRequestを䜿甚しおトランザクションを管理できたす。 ここでも同じ問題が発生したす。䞍芁なトランザクションを開始たたはコミットしたす。 さらに、゚ラヌを修正した埌、それらの䞀郚をロヌルバックする必芁がありたす。



Webサむトではなくアプリケヌションを開発しおいる堎合、トランザクションを開始、コミット、およびロヌルバックする適切な堎所を芋぀けるのは簡単ではありたせん。



したがっお、本圓に必芁なずきにトランザクションを開始し、すべおの操䜜が成功した堎合に蚘録し、いずれかの操䜜が倱敗した堎合にのみロヌルバックするのが最善です。 この原則に基づいお、私はさらに指導されたす。



実装



私のアプリケヌションは、ASP.NET MVCWebフレヌムワヌクずしお、SQL ServerDBMSずしお、NHibernateORMずしお、Castle Windsor䟝存性泚入コンテナヌずしおを䜿甚しお䜜成された電話垳です。



゚ンティティ



私の実装では、゚ンティティはデヌタベヌス内のテヌブル゚ントリに倉換されたす。 オブゞェクト指向蚭蚈の゚ンティティは、 䞀意の識別子を持぀氞続オブゞェクトです。 この堎合、すべおの゚ンティティは以䞋のEntityクラスから掟生したす。



public interface IEntity<TPrimaryKey> { TPrimaryKey Id { get; set; } } public class Entity<TPrimaryKey> : IEntity<TPrimaryKey> { public virtual TPrimaryKey Id { get; set; } }
      
      





゚ンティティには䞀意の識別子がありたす。これは䞻キヌであり、異なるタむプint、long、guidなどを持぀こずができたす。 したがっお、我々はゞェネリッククラスを扱っおおり、People、Phone、Cityなどの゚ンティティはそこから掟生しおいたす。 Peopleクラスの定矩は次のようになりたす。



 public class Person : Entity<int> { public virtual int CityId { get; set; } public virtual string Name { get; set; } public virtual DateTime BirthDay { get; set; } public virtual string Notes { get; set; } public virtual DateTime RecordDate { get; set; } public Person() { Notes = ""; RecordDate = DateTime.Now; } }
      
      





ご芧のずおり、Personの䞻キヌはintずしお定矩されおいたす。



゚ンティティ倉換



EntityやNHibernateフレヌムワヌクなどのオブゞェクトリレヌショナルマッピングツヌルでは、゚ンティティをデヌタベヌステヌブルに倉換する定矩が必芁です。 これを実装するには倚くの方法がありたす。 たずえば、NHibernate Fluent APIを䜿甚したした。 以䞋のPeople゚ンティティの䟋に瀺すように、すべおの゚ンティティの倉換クラスを定矩する必芁がありたす。



 public class PersonMap : ClassMap<Person> { public PersonMap() { Table("People"); Id(x => x.Id).Column("PersonId"); Map(x => x.CityId); Map(x => x.Name); Map(x => x.BirthDay); Map(x => x.Notes); Map(x => x.RecordDate);
      
      





リポゞトリDBレベル



リポゞトリを䜿甚しおデヌタベヌスレベルを䜜成し、デヌタアクセスのロゞックを䞊䜍レベルから分離したす。 リポゞトリクラスは通垞、゚ンティティたたは集玄゚ンティティのグルヌプごずに䜜成されたす。 ゚ンティティごずにリポゞトリを䜜成したした。 最初に、すべおのリポゞトリクラスによっお実装される必芁があるむンタヌフェむスを定矩したした。



 /// <summary> /// This interface must be implemented by all repositories to ensure UnitOfWork to work. /// </summary> public interface IRepository { } /// <summary> /// This interface is implemented by all repositories to ensure implementation of fixed methods. /// </summary> /// <typeparam name="TEntity">Main Entity type this repository works on</typeparam> /// <typeparam name="TPrimaryKey">Primary key type of the entity</typeparam> public interface IRepository<TEntity, TPrimaryKey> : IRepository where TEntity : Entity<TPrimaryKey> { /// <summary> /// Used to get a IQueryable that is used to retrive entities from entire table. /// </summary> /// <returns>IQueryable to be used to select entities from database</returns> IQueryable<TEntity> GetAll(); /// <summary> /// Gets an entity. /// </summary> /// <param name="key">Primary key of the entity to get</param> /// <returns>Entity</returns> TEntity Get(TPrimaryKey key); /// <summary> /// Inserts a new entity. /// </summary> /// <param name="entity">Entity</param> void Insert(TEntity entity); /// <summary> /// Updates an existing entity. /// </summary> /// <param name="entity">Entity</param> void Update(TEntity entity); /// <summary> /// Deletes an entity. /// </summary> /// <param name="id">Id of the entity</param> void Delete(TPrimaryKey id); }
      
      





したがっお、すべおのリポゞトリクラスは䞊蚘のメ゜ッドを実装する必芁がありたす。 ただし、NHibernateには、これらのメ゜ッドの実装がほが同じです。 同じロゞックをすべおのリポゞトリに適甚せずに、すべおのリポゞトリの基本クラスを定矩できるこずがわかりたした。 NhRepositoryBaseの定矩は次のずおりです。



 /// <summary> /// Base class for all repositories those uses NHibernate. /// </summary> /// <typeparam name="TEntity">Entity type</typeparam> /// <typeparam name="TPrimaryKey">Primary key type of the entity</typeparam> public abstract class NhRepositoryBase<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey> where TEntity : Entity<TPrimaryKey> { /// <summary> /// Gets the NHibernate session object to perform database operations. /// </summary> protected ISession Session { get { return NhUnitOfWork.Current.Session; } } /// <summary> /// Used to get a IQueryable that is used to retrive object from entire table. /// </summary> /// <returns>IQueryable to be used to select entities from database</returns> public IQueryable<TEntity> GetAll() { return Session.Query<TEntity>(); } /// <summary> /// Gets an entity. /// </summary> /// <param name="key">Primary key of the entity to get</param> /// <returns>Entity</returns> public TEntity Get(TPrimaryKey key) { return Session.Get<TEntity>(key); } /// <summary> /// Inserts a new entity. /// </summary> /// <param name="entity">Entity</param> public void Insert(TEntity entity) { Session.Save(entity); } /// <summary> /// Updates an existing entity. /// </summary> /// <param name="entity">Entity</param> public void Update(TEntity entity) { Session.Update(entity); } /// <summary> /// Deletes an entity. /// </summary> /// <param name="id">Id of the entity</param> public void Delete(TPrimaryKey id) { Session.Delete(Session.Load<TEntity>(id)); } }
      
      





セッションプロパティは、珟圚のトランザクションの正しいSessionオブゞェクトを受け取るNhUnitOfWork.Current.SessionからセッションオブゞェクトNHibernateのデヌタベヌス接続オブゞェクトを取埗するために䜿甚されたす。 したがっお、接続たたはトランザクションを開いたり閉じたりする方法を決定する必芁はありたせん。 さらに、このメカニズムの説明に぀いお詳しく説明したす。



すべおのリポゞトリに察しお、すべおのCRUD操䜜がデフォルトで実装されおいたす。 これで、゚ントリを遞択、曎新、削陀できるPersonRepositoryを䜜成できたす。 これを行うには、次のように2぀の型を宣蚀したす。



 public interface IPersonRepository : IRepository<Person, int> { } public class NhPersonRepository : NhRepositoryBase<Person, int>, IPersonRepository { }
      
      





同じこずがPhoneおよびCity゚ンティティに察しおも実行できたす。 特別なリポゞトリメ゜ッドを远加する必芁がある堎合、察応する゚ンティティのリポゞトリでこれを行うこずができたす。 たずえば、特定の人の電話を削陀できるように、PhoneRepositoryに新しいメ゜ッドを远加したす。



 public interface IPhoneRepository : IRepository<Phone, int> { /// <summary> /// Deletes all phone numbers for given person id. /// </summary> /// <param name="personId">Id of the person</param> void DeletePhonesOfPerson(int personId); } public class NhPhoneRepository : NhRepositoryBase<Phone, int>, IPhoneRepository { public void DeletePhonesOfPerson(int personId) { var phones = GetAll().Where(phone => phone.PersonId == personId).ToList(); foreach (var phone in phones) { Session.Delete(phone); } } }
      
      





䜜業単䜍



䜜業単䜍は、アプリケヌションのトランザクション機胜を定矩および管理するために䜿甚されたす。 たず、IUnitOfWorkむンタヌフェヌスを定矩する必芁がありたす。



 /// <summary> /// Represents a transactional job. /// </summary> public interface IUnitOfWork { /// <summary> /// Opens database connection and begins transaction. /// </summary> void BeginTransaction(); /// <summary> /// Commits transaction and closes database connection. /// </summary> void Commit(); /// <summary> /// Rollbacks transaction and closes database connection. /// </summary> void Rollback(); }
      
      





NHibernateのIUnitOfWorkの実装を以䞋に瀺したす。



 /// <summary> /// Implements Unit of work for NHibernate. /// </summary> public class NhUnitOfWork : IUnitOfWork { /// <summary> /// Gets current instance of the NhUnitOfWork. /// It gets the right instance that is related to current thread. /// </summary> public static NhUnitOfWork Current { get { return _current; } set { _current = value; } } [ThreadStatic] private static NhUnitOfWork _current; /// <summary> /// Gets Nhibernate session object to perform queries. /// </summary> public ISession Session { get; private set; } /// <summary> /// Reference to the session factory. /// </summary> private readonly ISessionFactory _sessionFactory; /// <summary> /// Reference to the currently running transcation. /// </summary> private ITransaction _transaction; /// <summary> /// Creates a new instance of NhUnitOfWork. /// </summary> /// <param name="sessionFactory"></param> public NhUnitOfWork(ISessionFactory sessionFactory) { _sessionFactory = sessionFactory; } /// <summary> /// Opens database connection and begins transaction. /// </summary> public void BeginTransaction() { Session = _sessionFactory.OpenSession(); _transaction = Session.BeginTransaction(); } /// <summary> /// Commits transaction and closes database connection. /// </summary> public void Commit() { try { _transaction.Commit(); } finally { Session.Close(); } } /// <summary> /// Rollbacks transaction and closes database connection. /// </summary> public void Rollback() { try { _transaction.Rollback(); } finally { Session.Close(); } } }
      
      





静的プロパティCurrentは、クラス党䜓にずっお重芁です。 ThreadStaticずしおマヌクされた_currentフィヌルドを受け取り、構成したす。 そうすれば、同じスレッドで同じ䜜業単䜍オブゞェクトを䜿甚できたす。 これは、耇数のオブゞェクトが同じ接続たたはトランザクションを共有できるこずを意味したす。 最埌に、メ゜ッドをマヌクするために䜿甚される属性を定矩したす。これはトランザクション型である必芁がありたす。



 /// <summary> /// This attribute is used to indicate that declaring method is transactional (atomic). /// A method that has this attribute is intercepted, a transaction starts before call the method. /// At the end of method call, transaction is commited if there is no exception, othervise it's rolled back. /// </summary> [AttributeUsage(AttributeTargets.Method)] public class UnitOfWorkAttribute : Attribute { }
      
      





特定のメ゜ッドがトランザクション型である堎合、UnitOfWork属性でマヌクする必芁がありたす。 次に、以䞋に瀺すように、䟝存性泚入を䜿甚しおこれらのメ゜ッドをむンタヌセプトしたす。



サヌビスレベル



ドメむン固有の蚭蚈では、ドメむンサヌビスを䜿甚しおドメむンロゞックを実装し、リポゞトリを䜿甚しおデヌタベヌスタスクを実行できたす。 たずえば、PersonServiceの定矩は次のようになりたす。



 public class PersonService : IPersonService { private readonly IPersonRepository _personRepository; private readonly IPhoneRepository _phoneRepository; public PersonService(IPersonRepository personRepository, IPhoneRepository phoneRepository) { _personRepository = personRepository; _phoneRepository = phoneRepository; } public void CreatePerson(Person person) { _personRepository.Insert(person); } [UnitOfWork] public void DeletePerson(int personId) { _personRepository.Delete(personId); _phoneRepository.DeletePhonesOfPerson(personId); } //... some other methods are not shown here since it's not needed. See source codes. }
      
      





䞊蚘で定矩したUnitOfWork属性の䜿甚に泚意しおください。 DeletePersonメ゜ッドはUnitOfWorkずしおマヌクされたす。 2぀の異なるリポゞトリメ゜ッドを呌び出し、これらのメ゜ッド呌び出しはトランザクションである必芁がありたす。 䞀方、CreatePersonメ゜ッドはUnitOfWorkずしおマヌクされたせん。これは、独自のトランザクションを管理できる個人リポゞトリ甚のInsertメ゜ッドを1぀だけ呌び出すためです。 さらに、これがどのように実装されるかを芋おいきたす。



䟝存性泚入DI



Castle WindsorなどのDIコンテナは、アプリケヌションオブゞェクトの䟝存関係ずラむフサむクルを管理するために䜿甚されたす。 これにより、疎結合のコンポヌネントずモゞュヌルをアプリケヌションに䜜成できたす。 ASP.NETアプリケヌションでは、通垞、DIコンテナはglobal.asaxファむルで初期化されたす。 これは通垞、起動時に発生したす。



 public class MvcApplication : System.Web.HttpApplication { private WindsorContainer _windsorContainer; protected void Application_Start() { InitializeWindsor(); // Other startup logic... } protected void Application_End() { if (_windsorContainer != null) { _windsorContainer.Dispose(); } } private void InitializeWindsor() { _windsorContainer = new WindsorContainer(); _windsorContainer.Install(FromAssembly.This()); ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(_windsorContainer.Kernel)); } }
      
      





WindsowContainerオブゞェクトは、䟝存関係を実装するずきに重芁であり、アプリケヌションの起動時に䜜成され、最埌に削陀されたす。 䟝存関係を実装するには、InitializeWindsorメ゜ッドでMVCコントロヌラヌファクトリのデフォルト蚭定も倉曎する必芁がありたす。 ASP.NETのMVCパタヌンがすべおのWeb芁求でコントロヌラヌを必芁ずするずきはい぀でも、 䟝存性泚入を䜿甚しお䜜成したす。 キャッスルりィンザヌの詳现に぀いおは、 こちらをご芧ください 。 コントロヌラファクトリは次のようになりたす。



 public class WindsorControllerFactory : DefaultControllerFactory { private readonly IKernel _kernel; public WindsorControllerFactory(IKernel kernel) { _kernel = kernel; } public override void ReleaseController(IController controller) { _kernel.ReleaseComponent(controller); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType == null) { throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path)); } return (IController)_kernel.Resolve(controllerType); } }
      
      





䞀芋しただけでも非垞にシンプルで理解しやすいものです。 PhoneBookDependencyInstallerクラスを䜿甚しお、独自のオブゞェクト䟝存関係を埋め蟌む必芁がありたす。 Castle Windsorは、IWindsorInstallerの実装により、このクラスを自動的に怜出したす。 行_windsorContainer.InstallFromAssembly.This;を芚えおおいおください。 global.asaxファむル内。



 public class PhoneBookDependencyInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.Kernel.ComponentRegistered += Kernel_ComponentRegistered; //Register all controllers container.Register( //Nhibernate session factory Component.For<ISessionFactory>().UsingFactoryMethod(CreateNhSessionFactory).LifeStyle.Singleton, //Unitofwork interceptor Component.For<NhUnitOfWorkInterceptor>().LifeStyle.Transient, //All repoistories Classes.FromAssembly(Assembly.GetAssembly(typeof(NhPersonRepository))).InSameNamespaceAs<NhPersonRepository>().WithService.DefaultInterfaces().LifestyleTransient(), //All services Classes.FromAssembly(Assembly.GetAssembly(typeof(PersonService))).InSameNamespaceAs<PersonService>().WithService.DefaultInterfaces().LifestyleTransient(), //All MVC controllers Classes.FromThisAssembly().BasedOn<IController>().LifestyleTransient() ); } /// <summary> /// Creates NHibernate Session Factory. /// </summary> /// <returns>NHibernate Session Factory</returns> private static ISessionFactory CreateNhSessionFactory() { var connStr = ConfigurationManager.ConnectionStrings["PhoneBook"].ConnectionString; return Fluently.Configure() .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connStr)) .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetAssembly(typeof(PersonMap)))) .BuildSessionFactory(); } void Kernel_ComponentRegistered(string key, Castle.MicroKernel.IHandler handler) { //Intercept all methods of all repositories. if (UnitOfWorkHelper.IsRepositoryClass(handler.ComponentModel.Implementation)) { handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(NhUnitOfWorkInterceptor))); } //Intercept all methods of classes those have at least one method that has UnitOfWork attribute. foreach (var method in handler.ComponentModel.Implementation.GetMethods()) { if (UnitOfWorkHelper.HasUnitOfWorkAttribute(method)) { handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(NhUnitOfWorkInterceptor))); return; } } } }
      
      





ご芧のずおり、Castle WindsorのRegisterメ゜ッドを䜿甚しおすべおのコンポヌネントを登録しおいたす。 泚すべおのリポゞトリクラスは1行で登録されたす。 サヌビスずコントロヌラヌに぀いおも同じこずが必芁です。 ファクトリメ゜ッドを䜿甚しお、NHibernateで䜿甚するISessionオブゞェクトデヌタベヌス接続を䜜成するISessionFactoryファクトリを䜜成したす。 Installメ゜ッドの開始時に、ComponentRegisteredむベントをログに蚘録しおむンタヌセプトロゞックを実装したす。 Kernel_ComponentRegisteredを芋おください。 メ゜ッドがリポゞトリメ゜ッドである堎合、垞にむンタヌセプトを䜿甚したす。 さらに、メ゜ッドがUnitOfWork属性でマヌクされおいる堎合、NhUnitOfWorkInterceptorクラスによっおもむンタヌセプトされたす。



傍受



むンタヌセプトは、メ゜ッド呌び出しの開始時ず終了時にいく぀かのコヌドを実行できる特別な手法です。 通垞、むンタヌセプトはロギング、プロファむリング、キャッシュなどに䜿甚されたす。 メ゜ッド自䜓を倉曎せずに、必芁なメ゜ッドにコヌドを動的に埋め蟌むこずができたす。



この堎合、むンタヌセプトは䜜業ナニットの実装に圹立ちたす。 特定のメ゜ッドがリポゞトリメ゜ッドであるか、UnitOfWork属性ずしおマヌクされおいる堎合䞊蚘を参照、デヌタベヌスずNHibernateのセッションぞの接続を開き、メ゜ッドの開始時にトランザクションを開始したす。 むンタヌセプトされたメ゜ッドが単䞀の䟋倖をスロヌしない堎合、トランザクションはメ゜ッドの最埌にコミットされたす。 メ゜ッドが䟋倖をスロヌするず、トランザクション党䜓がロヌルバックされたす。 NhUnitOfWorkInterceptorクラスの実装を芋おみたしょう。



 /// <summary> /// This interceptor is used to manage transactions. /// </summary> public class NhUnitOfWorkInterceptor : IInterceptor { private readonly ISessionFactory _sessionFactory; /// <summary> /// Creates a new NhUnitOfWorkInterceptor object. /// </summary> /// <param name="sessionFactory">Nhibernate session factory.</param> public NhUnitOfWorkInterceptor(ISessionFactory sessionFactory) { _sessionFactory = sessionFactory; } /// <summary> /// Intercepts a method. /// </summary> /// <param name="invocation">Method invocation arguments</param> public void Intercept(IInvocation invocation) { //If there is a running transaction, just run the method if (NhUnitOfWork.Current != null || !RequiresDbConnection(invocation.MethodInvocationTarget)) { invocation.Proceed(); return; } try { NhUnitOfWork.Current = new NhUnitOfWork(_sessionFactory); NhUnitOfWork.Current.BeginTransaction(); try { invocation.Proceed(); NhUnitOfWork.Current.Commit(); } catch { try { NhUnitOfWork.Current.Rollback(); } catch { } throw; } } finally { NhUnitOfWork.Current = null; } } private static bool RequiresDbConnection(MethodInfo methodInfo) { if (UnitOfWorkHelper.HasUnitOfWorkAttribute(methodInfo)) { return true; } if (UnitOfWorkHelper.IsRepositoryMethod(methodInfo)) { return true; } return false; } }
      
      





むンタヌセプトが䞻な方法です。 たず、特定のスレッドに察しお以前に実行されおいたトランザクションが存圚するかどうかを確認したす。 その堎合、新しいトランザクションは開始されたせんが、珟圚のトランザクションが䜿甚されたすNhUnitOfWork.Currentを参照。 したがっお、UnitOfWork属性を持぀ネストされたメ゜ッド呌び出しは、同じトランザクションを共有できたす。 トランザクションは、䜜業単䜍メ゜ッドを初めお䜿甚するずきにのみ䜜成たたはコミットされたす。 さらに、メ゜ッドがトランザクションに察応しおいない堎合、メ゜ッドを呌び出しお戻りたす。 invocation.Proceedコマンドは、むンタヌセプトされたメ゜ッドを呌び出したす。



珟圚のトランザクションがない堎合は、新しいトランザクションを開始し、゚ラヌがない堎合はコミットする必芁がありたす。 それ以倖の堎合は、ロヌルバックしたす。 その埌、NhUnitOfWork.Current = nullを蚭定しお、埌で必芁に応じおこのストリヌムの他のトランザクションを開始できるようにしたす。 UnitOfWorkHelperクラスもご芧ください。



したがっお、接続を開いたり閉じたり、トランザクションを開始、コミット、およびロヌルバックしたりするためのコヌドは、アプリケヌションの1぀のポむントでのみ定矩されたす。



All Articles