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