public interface IEntity<TPrimaryKey> { TPrimaryKey Id { get; set; } } public class Entity<TPrimaryKey> : IEntity<TPrimaryKey> { public virtual TPrimaryKey Id { get; set; } }
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; } }
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);
/// <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); }
/// <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)); } }
public interface IPersonRepository : IRepository<Person, int> { } public class NhPersonRepository : NhRepositoryBase<Person, int>, IPersonRepository { }
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); } } }
/// <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(); }
/// <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(); } } }
/// <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 { }
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. }
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)); } }
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); } }
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; } } } }
/// <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; } }
Source: https://habr.com/ru/post/344508/
All Articles