Dependency Injection and Unit of Work Using Castle Windsor and NHibernate

September 25, 2017 | Author: ZoranMatuško | Category: Database Transaction, Object Relational Mapping, Databases, Json, Application Programming Interface
Share Embed Donate


Short Description

Download Dependency Injection and Unit of Work Using Castle Windsor and NHibernate...

Description

Dependency Injection and Unit Of Work using Castle Windsor and NHibernate 

Download source - 962 KB

Contents   o o  o o o o o o o  o o  

Introduction Problems How to open/close connections How to manage transactions Implementation Entities Entity mappings Repositories (database layer) Unit of work Service layer Dependency injection Interception Web Application Server side Client side Console Application References

Introduction In this article, I will show an implementation of dependency injection, repository and unit of work patterns usingCastle Windsor as DI (dependency injection) container and NHibernate as ORM (Object Relational Mapping) tool. 

 

Dependency Injection: Dependency injection is a software design pattern that allows removing hard-coded dependencies and making it possible to change them, whether at run-time or compile-time [1] Repository: Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects. [2] Unit of work: Used to define and manage transactional jobs in your application. [4] You can find many resources, articles and tutorials on dependency injection and unit of work. So, I'll not define them here. This article will be focused on solution of problems described in the next section.

Problems When you are developing a data-driven application, you consider some design principles. In this section, I'll describe them and explain the solution briefly.

How to open/close connections First problem is that: How and where to open and close connections. Surely, it's best to manage connections in database layer (probably in repositories). So, we can open connection, run database command and close connection in every repository method call. This may be inefficient and useless if we want to share same connection on different methods of same repository or different methods of different repositories (Think a transaction that uses some methods of different repositories). If we are bulding a web site (ASP.NET MVC or Web Forms), we can open the connection on Application_BeginRequest and close it on Application_EndRequest. But this approach has some disadvantages:  

We open/close database for every request even if some request does not use database. So, a connection is taken from connection pool for a while even if it's not used. We open database at the beginning of the request and close it at the end. But, there maybe cases the request is very long and database operation takes a short time in that request. Again, an inefficient usage of connection pool. Issues described above may not be big problems for you (indeed, for me, they are problems). But if you are not developing a web site, if you are developing a windows service that runs many threads those are using database in some periods of time? So, where to open/close connection.

How to manage transactions If you are using transactions (most of applications do that) in your database operations, where to begin, commit or rollback the transaction. You can not do in repository methods since your transaction may include many different repository method calls. So, your business layer (that calls the repository methods) can begin/commit/rollback transactions. There are some problems in this approach:  

Your business layer includes database specific codes. This breaks single responsibility and layering. This approach dublicates transaction logic in every business method. As I described in the previous section, you can use Application_BeginRequest and Application_EndRequest to manage transactions. Same problems are here again: You can begin/commit unnecessary transactions. Also, you must handle errors and rollback transactions when needed. Also, if you are not developing a web site, it's hard to find a good place to begin/commit/rollback transactions.

So, best approach is to begin a transaction when you really need to begin, commit the transaction when and only if all of your operations succeed and the rollback transaction if any of your operations fail. In this article, I implemented right this approach.

Implementation I implemented a simple phone book application using ASP.NET MVC (as web framework), Sql Server (as DBMS),NHibernate (as ORM) and Castle Windsor (as Dependency Injection Container).

Entities In our implementation, Entity is mapped to a record in a database table. In domain driven design (DDD), Entity is a persistent object that has a unique identifier [3]. All Entities in our solution is derived from Entity class defined below:

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

An entity has a unique identifier (primary key) that's type can be different (int, long, guid...), so it's a generic class. Our entities (People, Phone, City...) are derived from this class. For example, People class is defined as below: public class Person : Entity { 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; } }

As you see, primary key of Person is defined as int.

Entity mappings

ORM tools (like Entity framework and NHibernate) requires definition of mapping of Entities to database tables. There are many ways to implement it. I used NHibernate Fluent API to accomplish that. So, we define a mapping class for all entitites as shown below for People entity: public class PersonMap : ClassMap { 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); } }

Repositories (Database layer) Repositories [2] are used to create database layer to abstract data access logic from upper layers. Generally a repository class is created for each entity (or aggregation: group of entities). I created a repository for each entity. First, I defined an interface that must be implemented by all repository classes: /// /// This interface must be implemented by all repositories to ensure UnitOfWork to work. /// public interface IRepository { } /// /// This interface is implemented by all repositories to ensure implementation of fixed methods. /// /// Main Entity type this repository works on /// Primary key type of the entity public interface IRepository : IRepository where TEntity : Entity { /// /// Used to get a IQueryable that is used to retrive entities from entire table. /// /// IQueryable to be used to select entities from database IQueryable GetAll(); /// /// Gets an entity. /// /// Primary key of the entity to get /// Entity TEntity Get(TPrimaryKey key); /// /// Inserts a new entity. /// /// Entity void Insert(TEntity entity);

/// Updates an existing entity. /// Entity void Update(TEntity entity); /// Deletes an entity. /// Id of the entity void Delete(TPrimaryKey id); }

Thus, all repository classes must implement methods listed above. But, implementation of these methods are almost same in NHibernate. So, we can define a base class for all repositories. Thus, we don't implement same logic for all repositories. See NhRepositoryBase defined as below: /// Base class for all repositories those uses NHibernate. /// Entity type /// Primary key type of the entity public abstract class NhRepositoryBase : IRepository where TEntity : Entity { /// Gets the NHibernate session object to perform database operations. protected ISession Session { get { return NhUnitOfWork.Current.Session; } } /// Used to get a IQueryable that is used to retrive object from entire table. /// IQueryable to be used to select entities from database public IQueryable GetAll() { return Session.Query(); } /// Gets an entity. /// Primary key of the entity to get public TEntity Get(TPrimaryKey key) { return Session.Get(key); } /// Inserts a new entity. /// Entity public void Insert(TEntity entity) { Session.Save(entity); } /// Updates an existing entity. /// Entity public void Update(TEntity entity) { Session.Update(entity); } /// Deletes an entity. /// Id of the entity public void Delete(TPrimaryKey id) { Session.Delete(Session.Load(id)); } }

Session property is used to get session object (database connection object in NHibernate) fromNhUnitOfWork.Current.Session. This gets the right Session object for current running

transaction, so repositories does not think how to open/close connection and transaction. This mechanism will be explained in later sections. As you see, all CRUD operations are implemented for all repositories as default. So, now we can create aPersonRepository that can select, delete, update and create records just declaring types shown below: public interface IPersonRepository : IRepository { } public class NhPersonRepository : NhRepositoryBase, IPersonRepository { }

We can do same for Phone and City entities. If we want to add a custom repository method, we can just add it to related entitiy's repository. For instance, we can add a new method to PhoneRepository to delete phones of given person: public interface IPhoneRepository : IRepository { /// /// Deletes all phone numbers for given person id. /// /// Id of the person void DeletePhonesOfPerson(int personId); } public class NhPhoneRepository : NhRepositoryBase, IPhoneRepository { public void DeletePhonesOfPerson(int personId) { var phones = GetAll().Where(phone => phone.PersonId == personId).ToList(); foreach (var phone in phones) { Session.Delete(phone); } } }

Unit of work Unit of work is used to define and manage transactional jobs in your application. We define IUnitOfWork interface to define these jobs:

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

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

/// Rollbacks transaction and closes database connection. public void Rollback() { try { _transaction.Rollback(); } finally { Session.Close(); } } }

Static Current property is the key point of the class. As you see, it gets/sets _current field that is marked asThreadStatic [5]. So, we can use same Unit of work object in same thread. Thus, we can share same connection/transaction between objects. Lastly, we define an attribute that is used to mark a method which must be transactional: /// 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. [AttributeUsage(AttributeTargets.Method)] public class UnitOfWorkAttribute : Attribute { }

If a method must be transactional, we just mark it with UnitOfWork attribute. Then we will intercept these methods using dependency injection as described later.

Service layer In Domain Driven Design, Domain services is used to implement business logic. It can use repositories to perform database tasks. For example, PersonService is defined as below: 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. }

Focus on usage of UnitOfWork attrbiute defined in the previous section. DeletePerson method is marked as UnitOfWork. Because it calls two different repository methods and these method calls must be transactional. On the other hand, CreatePerson is not UnitOfWork since it only calls one repository method (Insert of person repository) and this repository method can open/close (manage) it's own transaction. We will see how to implement it in next sections.

Dependency injection Dependency Injection (DI) Containers (like Castle Windsor we use) is used to manage dependencies and lifecycles of object is your application. So, it helps you to build loosely coupled components/modules in your application. It's generally initialized on startup of your application. In an ASP.NET application, global.asax is the mostly used place to initialize it: 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)); } }

We create a WindsowContainer object (this is the main object to use dependency injection) on application startup and dispose it at the end. In InitializeWindsor method we also change default controller factory of MVC to use dependency injection. So, whenever ASP.NET MVC needs a Controller (in every web request), it creates it using DI [6]. See http://docs.castleproject.org/Windsor.Windsor-tutorial-ASP-NET-MVC-3-application-To-beSeen.ashx to learn Castle windsor. Here, our controller factory: 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); } }

It's pretty simple and describes itself. The important part is to inject our own object dependencies. It's accomplished byPhoneBookDependencyInstaller class. This class is automatically investigated by Castle windsor since it implementsIWindsorInstaller. Remember the _windsorContainer.Install(FromAssembly.This()); line in global.asax file.

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().UsingFactoryMethod(CreateNhSessionFactory).LifeSt yle.Singleton, //Unitofwork interceptor Component.For().LifeStyle.Transient, //All repoistories Classes.FromAssembly(Assembly.GetAssembly(typeof(NhPersonRepository))).InSameNames paceAs().WithService.DefaultInterfaces().LifestyleTransient(), //All services Classes.FromAssembly(Assembly.GetAssembly(typeof(PersonService))).InSameNamespaceA s().WithService.DefaultInterfaces().LifestyleTransient(), //All MVC controllers Classes.FromThisAssembly().BasedOn().LifestyleTransient() ); } /// /// Creates NHibernate Session Factory. /// /// NHibernate Session Factory 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; } } } }

As you see we are registering all components using Windsor's Register method. Notice that weare registering allrepository classes with single line. We do same for services and controlles. We're using factory method to createISessionFactory that is used to create ISession (database connection) objects to use with NHibernate. Finally, at the begining of Install method, we register to Kernel's ComponentRegistered event to inject our interception logic. SeeKernel_ComponentRegistered method. If a method is a repository method, we are always using interception for it. Also, if a method has marked with UnitOfWork attribute, it's also intercepted with our NhUnitOfWorkInterceptorclass.

Interception

Interception is a technique to run some codes at the begining of a method call and at the end of the method call. It's generally used for logging, profiling, caching... etc. Using this technique you can dynamically inject your codes into desired methods without changing the method. We use interception to implement Unit of work. If a method is repository method or marked as UnitOfWork attribute (explained in previous section), we open a database connection (Session on NHibernate) and begin a transaction at the begining of the method. If no exception is thrown by the intercepted method, the transaction is commited at the end of the method. If the method throws any exception, whole transaction is rolled back. So, let's seeNhUnitOfWorkInterceptor class to know how I implemented it: /// This interceptor is used to manage transactions. public class NhUnitOfWorkInterceptor : IInterceptor

{ private readonly ISessionFactory _sessionFactory; /// Creates a new NhUnitOfWorkInterceptor object. public NhUnitOfWorkInterceptor(ISessionFactory sessionFactory) { _sessionFactory = sessionFactory; } /// Intercepts a method. 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; } }

The main method is Intercept. It first checks if there is a transaction started before for this thread. If so, it does not starts a new transaction, uses the current transaction (see NhUnitOfWork.Current). Thus, nested calls to methods those have UnitOfWork attribute can use/share same transaction. A transaction is created/commited in only first entrance of a unit of work method. Also, if a method is not transactional, it just calls the method and returns.invocation.Proceed() command perform a call to the intercepted method. If there is no transaction currently, we must start a new transaction and commit the transaction if no error, rollback the transaction on an error case. Finally we set the NhUnitOfWork.Current = null to allow to start other transactions for current thread later if needed. You can also see UnitOfWorkHelper class. Thus, the only open/close connection and begin/commit/rollback transaction codes in entire application is defined in only one point.

Web Application I developed a simple phone book application using the patterns described above. It's an ASP.NET MVC application. So, server side is implemented by ASP.NET MVC Controllers, client side is implemented using HTML and Javascript. To run application, first create the database using PhoneBookDb.sql file in the solution (it requires SQL Server 2008 R2 or a newer version). Then check the connection string in web.config if you change the name of the database. Also, it may require to Enable Nuget Package Restore to restore nuget packages.

Server side In our application (for demonstration purposes), we define just one controller: HomeController. It gets needed services in it's constructor. Castle Windsor injects these services into controller since we registered service classes and MVC controller factory to Windsor. Collapse | Copy Code

public class HomeController : Controller { private readonly IPersonService _personService; private readonly IPhoneService _phoneService; private readonly ICityService _cityService; public HomeController(IPersonService personService, ICityService cityService, IPhoneService phoneService) { _personService = personService;

_cityService = cityService; _phoneService = phoneService; } public ActionResult Index() { return View(); } public JsonResult PeopleList() { try { var personList = _personService.GetPeopleList(); return Json(new { Result = "OK", Records = personList }); } catch (Exception ex) { return Json(new {Result = "ERROR", Message = ex.Message}); } } public JsonResult DeletePerson(int id) { try { _personService.DeletePerson(id); return Json(new { Result = "OK" }); } catch (Exception ex) { return Json(new { Result = "ERROR", Message = ex.Message }); } } //Other actions... }

As you see, we use JSON objects to communicate with the client. Web page makes AJAXrequestto this controller to perform CRUD (Create, Retrive (select), Update and Delete) operations. See source codes for other actions.

Client side Client side is HTML and Javascript. I implemented client side using jTable [7]. It's a jQuery plugin that is developed by me. It automates listing of database records and update/create/delete forms. See jtable.org for more information. See source codes for implementation. Here is the result:

Console Application In this section, I'll show how to implement all techniques (mentioned above) for a console application. I created a simple console application that periodically runs, checks and deletes all people who are older than 120 age (Think that I don't want to store old people in my phone book). This is just an example for services those run on background and performs some tasks. It may be a console application or a background Windows Service. Does not matter. Here, our main class that prepares and runs dependency injection and starts our periodically running service: public class PhoneBookRunner : IDisposable { private WindsorContainer _windsorContainer; public void Start() { _windsorContainer = new WindsorContainer();

_windsorContainer.Install(FromAssembly.Containing()) ; _windsorContainer.Install(FromAssembly.This()); _windsorContainer.Resolve().Start(); } public void Dispose() { if (_windsorContainer != null) { _windsorContainer.Resolve().Stop(); _windsorContainer.Dispose(); } } }

In the Start method, we initialize the Castle Windsor container. Then we are registering dependencies using Installers. See source codes for Program.cs that uses PhoneBookRunner class. PhoneBookDependencyInstaller registers main components of our application. I also added a new installer for this console application: public class BackgroundServiceInstaller : IWindsorInstaller {

public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register( Component.For().ImplementedBy() .LifestyleTransient(), Component.For().ImplementedBy().L ifestyleSingleton() ); } }

PeriodicServiceTrigger uses a Timer to perform periodic checks:

public class PeriodicServiceTrigger : IPeriodicServiceTrigger { private readonly IDeleteOldRecordsService _deleteOldRecordsService; private readonly Timer _timer; public PeriodicServiceTrigger(IDeleteOldRecordsService deleteOldRecordsService) { _deleteOldRecordsService = deleteOldRecordsService; _timer = new Timer(10000); _timer.Elapsed += Timer_Elapsed; } public void Start() { _timer.Start(); } public void Stop() { _timer.Stop(); } private void Timer_Elapsed(object sender, ElapsedEventArgs e) { _deleteOldRecordsService.DeletePeopleOlderThan120Age(); } }

And finally we have a IDeleteOldRecordsService that is used to implement business rule: public class DeleteOldRecordsService : IDeleteOldRecordsService { private readonly IPersonRepository _personRepository; private readonly IPersonService _personService; public DeleteOldRecordsService(IPersonRepository personRepository, IPersonService personService) { _personRepository = personRepository; _personService = personService; } [UnitOfWork] public void DeletePeopleOlderThan120Age() {

var yearAge120 = DateTime.Now.AddYears(-120); var oldRecords = _personRepository.GetAll().Where(person => person.BirthDay < yearAge120).ToList(); foreach (var oldRecord in oldRecords) { _personService.DeletePerson(oldRecord.Id); } } }

As you see, DeletePeopleOlderThan120Age is a UnitOfWork method. So, it runs as transactional.

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF