Introduction

In these days i started use EF code first and think about how could i take the advantage form it for my recent project. My project is base on MVC3 and there are some challenges to continue update this project. At that time i must finish my job in a very shortly deadline so fast so good i just define some data access interfaces and implemented them with EF4.
There are some things in my project troubling me: 

  •     I have used the EF entities in my controllers anywhere that there is a connection translation error throwing infrequently. 
  •     Modules coupling degree becoming closed. 
  •     Manage services and contexts life cycle are difficult.

Obviously it needs to refactory my project with a new architecture designing. 

Preparing

Before start my work i need to do some researching and learning. I have read a large numbers of articles about DDD, SOA,EF code first, Dependency Injection pattern, Repository pattern even watch all web casts about MVC on ASP.NET and a week later ....

I have really found some useful resources about those topics and i sharing them below: 

Repository Pattern with EF Code first 

When i finish my learning on EF code first and Repository i couldn't found any solutions for implement Repository pattern with EF code first. EF4.1 is so powerful for building the DAL that it allows us using POCO objects to define our database and BO(business objects.) instead of inherit from Entity that could be give me a big hand. I could very easy to define the repository interface like below:

public interface IRepository<T>: IDisposable  where T : class
{
        IQueryable<T> All();

        IQueryable<T> Filter(Expression<Func<T, bool>> predicate);

        IQueryable<T> Filter<Key>(Expression<Func<T, bool>> filter , out int total, int index = 0, int size = 50);

        bool Contains(Expression<Func<T, bool>> predicate);

        T Find(params object[] keys);

        T Find(Expression<Func<T, bool>> predicate);

        T Create(T t);
        
        void Delete(T t);

        int Delete(Expression<Func<T, bool>> predicate);

        int Update(T t);

        int Count { get; }
}
		

Dependency Injection in MVC3

In Mvc3 we could use the DependencyResolver.SetResolver(IDependencyResolver resolver) to register a Dependency Injection Container and use IDependencyResolver.GetService() method to locate our registered service instance that is a very amazing feature in this version. For more about DI in MVC3 you could read the "ASP.NET MVC 3 Service Location" below.

Unity

We could found many popular DI framework in google such as  Castle Windsor, Structuremap,ObjectBuilder, Managed Extensibility Framework (MEF) and Microsoft Unity, I'd like to use Microsoft Unity 2.0 because the MVC DI features in comes from it that means we could very easy to implement DI in our Mvc applications.

References from MSDN: 

Unity is a lightweight, extensible dependency injection container that supports interception, constructor injection, property injection, and method call injection. You can use Unity in a variety of different ways to help decouple the components of your applications, to maximize coherence in components, and to simplify design, implementation, testing, and administration of these applications.

Unity is a general-purpose container for use in any type of Microsoft® .NET Framework-based application. It provides all of the features commonly found in dependency injection mechanisms, including methods to register type mappings and object instances, resolve objects, manage object lifetimes, and inject dependent objects into the parameters of constructors and methods and as the value of properties of objects it resolves.
In addition,

Unity is extensible. You can write container extensions that change the behavior of the container, or add new capabilities. For example, the interception feature provided by Unity, which you can use to add policies to objects, is implemented as a container extension. 

Implementation

Now let's go to create a demo to implement the Repository pattern with EF4.1 and DI in our Mvc3 application. There are 2 projects in my Demo solution they are :

  •     Demo.DAL - Contains IRepository and POCO objects.
  •     Demo.Web - The Mvc3 web application. 


We are already define the IRepository interface above, now we need to implement the IRepository.

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Data.Entity;
using System.Collections.Generic;
using System.Data;

namespace Demo.DAL
{
    public class Repository<TObject> : IRepository<TObject>
        where TObject : class
    {
        protected DB Context;

        public Repository()
        {
            Context = new DB();
            Context.Configuration.AutoDetectChangesEnabled = false;
        }

        protected DbSet<TObject> DbSet { get { return Context.Set<TObject>(); } }

        public void Dispose()
        {
            if (Context != null)
                Context.Dispose();
        }

        public virtual IQueryable<TObject> All()
        {
            return DbSet.AsNoTracking();
        }

        public virtual IQueryable<TObject> Filter(Expression<Func<TObject, bool>> predicate)
        {
            return DbSet.Where(predicate).AsQueryable<TObject>().AsNoTracking<TObject>();
        }

       
 public virtual IQueryable<TObject> 
Filter(Expression<Func<TObject, bool>> filter, out int 
total, int index = 0, int size = 50)
        {
            int skipCount = index * size;
            var _resetSet = filter != null ? DbSet.Where(filter).AsQueryable() : DbSet.AsQueryable();
            _resetSet = skipCount == 0 ? _resetSet.Take(size) : _resetSet.Skip(skipCount).Take(size);
            total = _resetSet.Count();
            return _resetSet.AsNoTracking().AsQueryable();
        }

        public bool Contains(Expression<Func<TObject, bool>> predicate)
        {
            return DbSet.Count(predicate) > 0;
        }

        public virtual TObject Find(params object[] keys)
        {
            return DbSet.Find(keys);
        }

        public virtual TObject Find(Expression<Func<TObject, bool>> predicate)
        {
            return DbSet.FirstOrDefault(predicate);
        }

        public virtual TObject Create(TObject TObject)
        {
            var newEntry = DbSet.Add(TObject);
            Context.SaveChanges();
            return newEntry;
        }

        public virtual int Count
        {
            get
            {
                return DbSet.Count();
            }
        }

        public virtual int Delete(TObject TObject)
        {
            var entry = Context.Entry(TObject);
            if (entry.State == EntityState.Detached)
                DbSet.Attach(TObject);

            DbSet.Remove(TObject);
            return Context.SaveChanges();
        }

        public virtual int Update(TObject TObject)
        {
            var entry = Context.Entry(TObject);
            DbSet.Attach(TObject);
            entry.State = EntityState.Modified;
            return Context.SaveChanges();
        }

        public virtual int Delete(Expression<Func<TObject, bool>> predicate)
        {
            var objects = Filter(predicate);
            foreach (var obj in objects)
                DbSet.Remove(obj);
            return Context.SaveChanges();
        }
    }

} 

Please notes that i have disable the AutoDetectChangeEnabled for dbContext and return all collection as AsNoTracking() ,AutoDetectChangesEnabled = false . Because the data tracking is an evil for ASP.NET application it will be harm for performance. Now i create the Product and Category objects and DbContext like below:

 namespace Demo.DAL
{
    public class Category
    {
        [Key]
        public int ID { get; set; }

        public string Name { get; set; }

        public string Title { get; set; }
    }

    public class Product
    {
        [Key]
        public int ID { get; set; }
        
        public int CategoryID { get; set; }

        [ForeignKey("CategoryID")]
        public virtual Category Category {get;set;}

        public string Name { get; set; }

        public string Title { get; set; }

        public string Description{get;set;}

        public decimal Price { get; set; }
    }

    public class DB : DbContext
    {
        public DB() : base("DemoDB") { }
        public DbSet<Category> Categories;
        public DbSet<Product> Products;
    }
} 


The DAL has been done! Constantly we need to create the SL (Service Layer) but now in this demo i just have one service : ICatalogService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Practices.Unity;
using Demo.DAL;

namespace Demo.Services
{
    public interface ICatalogService
    {
        List<Category> GetCategories();
        List<Product> GetProducts();
        Product AddProduct(Product product);
    }

    public class CatalogService:ICatalogService
    {
        [Dependency]
        public IRepository<Category> Categories { get; set; }

        [Dependency]
        public IRepository<Product> Products { get; set; }

        public List<Category> GetCategories()
        {
            return Categories.All().ToList();
        }

        public List<Product> GetProducts()
        {
            return Products.All().ToList();
        }

        public Product AddProduct(Product product)
        {
            return Products.Create(product);
        }
    }
}  

The next step is create a DependencyResolver for MVC : 

namespace Demo.Web
{
    public class UnityDependencyResolver : IDependencyResolver
    {
        readonly IUnityContainer _container;

        public UnityDependencyResolver(IUnityContainer container)
        {
            this._container = container;
        }

        public object GetService(Type serviceType)
        {
            try
            {
                return _container.Resolve(serviceType);
            }
            catch
            {
                return null;
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            try
            {
                return _container.ResolveAll(serviceType);
            }
            catch
            {
                return new List<object>();
            }
        }
    }
} 

Open the Global.asax and register types and set DependencyReslover in Application_Start

protected void Application_Start() {
            AreaRegistration.RegisterAllAreas();
            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            var container = new UnityContainer();
            container.RegisterType<ICatalogService, CatalogService>(new PerThreadLifetimeManager())
                          .RegisterType<IRepository<Category>, Repository<Category>>()
                          .RegisterType<IRepository<Product>, Repository<Product>>();
            DependencyResolver.SetResolver(new UnityDependencyResolver(container));
} 

Finally we could inject the ICatalogService in HomeController now.

public class HomeController : Controller
{
        private ICatalogService service;

        [Dependency]
        public ICatalogService Service
        {
            get { return service; }
            set { service = value; }
        }

        public ActionResult Index()
        {
            ViewBag.Message = "Welcome to ASP.NET MVC!";
            ViewData.Model = Service.GetProducts();
            return View();
        }

        public ActionResult About()
        {
            return View();
        }
}

 That's all enjoy! 

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"