Saturday, October 4, 2014

DryIoc + ASP.NET MVC + NHibernate boilerplate code

IoC wiring:

using Erp.Controllers;

using DryIoc;

using System;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

using System.Linq;


namespace Erp
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {

        
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);


            var ioc = new DryIocDependencyResolver();
            ControllerBuilder.Current.SetControllerFactory(ioc);          
        }
    }


    class DryIocDependencyResolver : System.Web.Mvc.DefaultControllerFactory
    {
        DryIoc.Container _container;

        public DryIocDependencyResolver()
        {
            _container = new DryIoc.Container();
            RegisterTheIocs();
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            System.Web.Mvc.IController ic = controllerType == null
                ? null
                : (System.Web.Mvc.IController)_container.Resolve(controllerType);       

           // _container.ResolvePropertiesAndFields(ic);  // uncomment this if you want to use DI on controller's properties

           return ic;
        }

        void RegisterTheIocs()
        {
            // ISessionFactory definition

            System.Reflection.Assembly assembly = typeof(HomeController).Assembly;

            foreach (var controller in assembly.GetTypes().Where(t => typeof(Controller).IsAssignableFrom(t)))
            {
                _container.Register(controller, DryIoc.Reuse.InResolutionScope);                
            }


            // _container.Register(typeof(Elmah.Mvc.ElmahController), DryIoc.Reuse.InResolutionScope);

            // http://www.ienablemuch.com/2013/11/typical-nhibernate-sessionfactory.html
            _container.RegisterDelegate<NHibernate.ISessionFactory>(x => Erp.DomainMapping.Mapper.SessionFactory, DryIoc.Reuse.Singleton);

            _container.Register<UnitTestFriendlyDal.IDataStore, UnitTestFriendlyDal.DataStore>(DryIoc.Reuse.InResolutionScope);
        }

  
    }
}



Sample Controller, we need to call _ds.Dispose() to effect the transaction:
using Erp.Domain.TheModels;
using Erp.Dto.Dtos;

using UnitTestFriendlyDal;

using System.Web.Mvc;


namespace Erp.Controllers
{
    public class CompaniesController : Controller
    {
        IDataStore _ds;

        public CompaniesController(IDataStore ds)
        {
            _ds = ds;
        }
        

        public ViewResult Summary(int id = 0)
        {
            var company = _ds.Get<company>(id);
            
            var companyDto = company.Summary(_ds);

            return View(companyDto);
        }

        
        public ViewResult Search(SearchedCompanyDto sc)
        {
            Company.SearchCompanies(_ds, sc);            

            return View(sc);
        }


        protected override void Dispose(bool disposing)
        {
            _ds.Dispose();
            base.Dispose(disposing);
        }
    }
}



IDataStore definition:
using System;
using System.Linq;

using NHibernate.Linq;

namespace UnitTestFriendlyDal
{
    public interface IDataStore : IDisposable
    {
        IQueryable<T> Query<T>();
        T Get<T>(object id);
        T Load<T>(object id);
        object Save(object transientObject);
    }

    public class DataStore : IDataStore
    {

        NHibernate.ISessionFactory _sessionFactory;
        NHibernate.ISession _session;
        NHibernate.ITransaction _transaction;

        public DataStore(NHibernate.ISessionFactory sessionFactory)
        {
            _sessionFactory = sessionFactory;
            _session = _sessionFactory.OpenSession();
            _transaction = _session.BeginTransaction();
        }


        IQueryable<T> IDataStore.Query<T>()
        {
            return _session.Query<T>();
        }


        T IDataStore.Get<T>(object id)
        {
            return _session.Get<T>(id);
        }

        T IDataStore.Load<T>(object id)
        {
            return _session.Load<T>(id);
        }

        object IDataStore.Save(object transientObject)
        {
            return _session.Save(transientObject);
        }

        
        // Because transaction is a cross-cutting concern. It should be automated
        void IDisposable.Dispose()
        {
             // http://www.hibernatingrhinos.com/products/nhprof/learn/alert/donotuseimplicittransactions
                    
            _transaction.Commit();
            _transaction.Dispose();
            _session.Dispose();
        }




    }



    /// <summary>
    /// cross-cutting concern    
    /// MakeCacheable replaces Cacheable, so IQueryable detection provider can be done here
    /// Can't use NHibernate's built-in .Cacheable on non-NHibernate IQueryable, it will throw an error    
    /// </summary>
    public static class NHibernateLinqExtensionMethods
    {
        public static IQueryable<T> MakeCacheable<T>(this IQueryable<T> query)
        {
            if (query.Provider.GetType() == typeof(NHibernate.Linq.DefaultQueryProvider))
                query = query.Cacheable();

            return query;
        }


    }

}



Sample Domain Model:
using Erp.Dto.Dtos;

using UnitTestFriendlyDal;


using System.Linq;



[assembly:System.Runtime.CompilerServices.InternalsVisibleTo("Erp.Domain.Test")]

namespace Erp.Domain.TheModels
{
    
    // What is Anemic Domain Model
    // http://en.wikipedia.org/wiki/Anemic_domain_model
    // http://www.martinfowler.com/bliki/AnemicDomainModel.html    

    // this is a non-Anemic Domain Model
    public class Company
    {
        // Use Virtuosity.Fody to eliminate the use of virtual keywords on NHibernate
        // https://www.nuget.org/packages/Virtuosity.Fody/
        // http://github.com/Fody/Virtuosity

        public int CompanyId { get; protected internal set; }
        public string CompanyName { get; protected internal set; }
        public string CompanyUrl { get; protected internal set; }
        public byte[] TinyLogo { get; protected internal set; }



        public static void SearchCompanies(IDataStore ds, SearchedCompanyDto sc)
        {           
            var q = from c in ds.Query<Company>().MakeCacheable()
                    where string.IsNullOrWhiteSpace(sc.q) || c.CompanyName.StartsWith(sc.q)
                    select c;
            
            // http://www.ienablemuch.com/2014/10/proper-way-to-query-reference-entity.html
            sc.Companies = q.ToList().Select(c => new CompanyDto { CompanyId = c.CompanyId, CompanyName = c.CompanyName, TinyLogo = c.TinyLogo });            
        }

        // Just ready this parameter IDataStore,
        // so if this one entity need  information from another domain model(s) that is not in its navigable properties, 
        // it's easy to access those information
        public CompanyDto Summary(IDataStore ds)
        {                        
            return new CompanyDto { CompanyName = this.CompanyName };
        }
    }
}




Sample Unit Test:
using Erp.Domain.TheModels;

using UnitTestFriendlyDal;

using Erp.Dto.Dtos;

using Moq;

using System.Linq;


using Microsoft.VisualStudio.TestTools.UnitTesting;



namespace Erp.Domain.Test
{
    [TestClass]
    public class UnitTest
    {
        [TestMethod]
        public void Test_if_search_code_is_done()
        {

            // Arrange
            var companies = new[] {
                new Company { CompanyName = "Alpha" },
                new Company { CompanyName = "Beta" },
                new Company { CompanyName = "Delta" },
                new Company { CompanyName = "Atlantis" },
            }.AsQueryable();


            var ds = new Mock<IDataStore>();
            ds.Setup(x => x.Query<Company>()).Returns(companies);



            // Act
            var sc = new SearchedCompanyDto { q = "A" };
            Company.SearchCompanies(ds.Object, sc);



            // Assert
            Assert.AreEqual(2, sc.Companies.Count());
        }
    }
}



Happy Coding!

No comments:

Post a Comment