Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MVC Repository Pattern with Entity Framework and solving Dependency Injection using Autofac: Part-I

0.00/5 (No votes)
20 Mar 2013 1  
Here I shall try to demonstrate the basic configuration of Autofac for Dependency Injection and Entity Framework to interact with storage.

Introduction

  • In my first post "Repository Pattern with Entity Framework Using EntityTypeConfiguration" I tried to present how to integrate the Repository pattern with EntityFramework.
  • Here I shall focus on handling Dependency Injection with the Autofac library in ASP.NET MVC 3.
  • In this part I shall emphasize on Container and Autofac configuration. But will not cover InstanceScope. I will try to cover it in the next article.

Please check my previous article "Repository Pattern with Entity Framework Using EntityTypeConfiguration", that would be helpful for continuity. 

Background   

I am not willing to elaborate on Dependency Injection with Autofac because a really very good article on it written by Nicholas Blumhardt is available here for beginners.  It is very helpful to understand why dependency injection is required. IoC is a very popular term with dependency injection.

After finishing this practice we are at least able to start moving to dependency injection using Autofac. So it is our starting point. 

Like the previous example I shall try to make the article concise so that audience can easily grab it.

After finishing this 

We are able to configure Dependency Injection using Autofac.

Required development tools

  • Visual Studio: 2010  and C# language 
  • SQL Server: 2008. Express edition may also work.
  • MVC-3
  • Autofac libraries
  • Autofac Integration MVC libraries
  • Entity Frame work libraries as we shall store data using repository pattern

Using the code

We already have a repository created in the Repository Pattern with Entity Framework Using EntityTypeConfiguration article. We shall use the same repository pattern here and  only work on two classes Category and Product for simplicity.

Now need a service layer that will prevent us directly using Repository in implementation scope. So that layer will handle all kind of operation regarding storage and business logic.

Our solution tree will be look like the following:

  • Two new projects "EfRepPatTest.Service" a class library and "Web.Implementation" as MVC3 Web Application.
  • Remove the "EfRepPatTest.Implementation" from the previous project. 

Source code provided at the top of this article.

EfRepPatTest.Service contains two classes and two interfaces for Category and Product. We have to add references to EfRepPatTest.Data and EfRepPatTest.Entity in here.

ICategoryService.cs

public interface ICategoryService
{
    List<Category> GetAll();
    Category GetById(int id);
    void Insert(Category model);
    void Update(Category model);
    void Delete(Category model);
}

CategoryService.cs will consume this Interface.

public class CategoryService:ICategoryService
{
    private IRepository<Category> categroyRepository;

    public CategoryService(IRepository<Category> categroyRepository)
    {
        this.categroyRepository = categroyRepository;
    }
    public List<Category> GetAll()
    {
        return categroyRepository.GetAll().ToList();
    }

    public Category GetById(int id)
    {
        if (id == 0)
            return null;
        return categroyRepository.GetById(id);
    }

    public void Insert(Category model)
    {
        if (model == null)
            throw new ArgumentNullException("category");
        categroyRepository.Insert(model);
    }
 
    public void Update(Category model)
    {
        if (model == null)
            throw new ArgumentNullException("category");
        categroyRepository.Update(model);

    }

    public void Delete(Category model)
    {
        if (model == null)
            throw new ArgumentNullException("category");
        categroyRepository.Delete(model);
    }
}

IProductService.cs

public interface IProductService
{
    List<Product> GetAll();
    Product GetById(Int64 id);
    void Insert(Product model);
    void Update(Product model);
    void Delete(Product model);
}

ProductService.cs is the same as the CategoryService class.

public class ProductService:IProductService
{
    private IRepository<Product> productRepository;
    public ProductService(IRepository<Product> productRepository)
    {
        this.productRepository = productRepository;
    }

    public List<Product> GetAll()
    {
        return productRepository.GetAll().ToList();
    }

    public Product GetById(Int64 id)
    {
        if (id == 0)
            return null;
        return productRepository.GetById(id);
    }

    public void Insert(Product model)
    {
        if (model == null)
            throw new ArgumentNullException("product");
        productRepository.Insert(model);
    }

    public void Update(Product model)
    {
        if (model == null)
            throw new ArgumentNullException("product");
        productRepository.Update(model);
    }

    public void Delete(Product model)
    {
        if (model == null)
            throw new ArgumentNullException("product");
        productRepository.Delete(model);
    }
}

First of all why is dependency injection required? In the article Repository Pattern with Entity Framework Using's project "EfRepPatTest.Implementation>Program.cs" we have created an instance of  DataContext, RpositoryService<Category> and etc. Look at the following code snippet:

EfRepPatTest.Implementation>Program.cs

class Program
{
    static void Main(string[] args)
    {
        var context = new DataContext();
        var dataBaseInitializer = new DataBaseInitializer();
        dataBaseInitializer.InitializeDatabase(context);

        var categoryRepository = new RepositoryService<Category>(context);

        //Adding category in the category entity
        var category = new Category()
        {
            Name = "Baverage"
        };

When we work with lots of classes and need to create an instance each time while consuming them from each other, each time we have to create an instance means the dependency will be handled manually.

Look back to the CategoryService class' constructor.

public class CategoryService:ICategoryService
{
    private IRepository<Category> categroyRepository;

    public CategoryService(IRepository<Category> categroyRepository)
    {
        this.categroyRepository = categroyRepository;
    }

We have to supply a parameter while calling CategoryService, remember it.

Let's look at ProductController.cs:

public class ProductController : Controller
{
    private IProductService productService;
    public ProductController(IProductService productService)
    {
        this.productService = productService;
    }
    public ActionResult Index()
    {
        ViewBag.Message = "Welcome to Dependency Injection Testing with Autofac";
        var products = productService.GetAll().ToList();

        var producModelList =
            products.Select(p =>
                {
                    var productModel = new ProductModel();
                    productModel.Id = p.Id;
                    productModel.Name = p.Name;
                    productModel.CategoryId = p.CategoryId;
                    productModel.MinimumStockLevel = p.MinimumStockLevel;


                    return productModel;
                }
                );
        return View(producModelList);
    }

}

ProductController is dependent on the ProductService class to serve product info.

Autofac will help us to resolve the dependency problem. There are a couple of light-weight libraries to do so.

Web.Implementation MVC project:

First of all create two classes in the Infrastructure  folder of the MVC project, named Dependency.cs and DependencyConfigure.cs. These classes hold the configuration for Dependency Injection.

Dependency.cs 

public class Dependency : IDependencyResolver
{
    private readonly IContainer container;

    public Dependency(IContainer container)
    {

        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        return
            container.IsRegistered(serviceType)
                ? container.Resolve(serviceType)
                : null;
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {

        Type enumerableServiceType =
            typeof (IEnumerable<>).MakeGenericType(serviceType);

        object instance =
            container.Resolve(enumerableServiceType);

        return ((IEnumerable) instance).Cast<object>();
    }
}

DependencyConfigure.cs 

internal class DependencyConfigure
{
    public static void Initialize()
    {
        var builder = new ContainerBuilder();
        DependencyResolver.SetResolver(
            new Dependency(RegisterServices(builder))
            );
    }

    private static IContainer RegisterServices(ContainerBuilder builder)
    {

        builder.RegisterAssemblyTypes(
            typeof(MvcApplication).Assembly
            ).PropertiesAutowired();

        //deal with your dependencies here
        builder.RegisterType<DataContext>().As<IDbContext>().InstancePerDependency();

        builder.RegisterGeneric(typeof(RepositoryService<>)).As(typeof(IRepository<>));

        builder.RegisterType<ProductService>().As<IProductService>();
        builder.RegisterType<CategoryService>().As<ICategoryService>();


        return
            builder.Build();
    }
}

The class Dependency.cs in the Infrastructure folder will implement the IDependencyResolver interface which belongs to the System.Web.Mvc namespace.

ContainerBuilder: Used to build an Autofac.IContainer from component registration

var builder = new ContainerBuilder();
  • DependencyResolver: Provides a registration point for dependency resolvers that implement the System.Web.Mvc.IDependencyResolver interface.
  • IDependencyResolver: Defines the method that simplifies service location and dependency resolution.
  • Here the Dependency class implements the IDependencyResolver interface.
  • DependencyResolver.SetResolver: Provides a registration point for dependency resolvers, using the specified dependency resolver interface. Here “specified dependency resolver interface” means the Dependency class as parameter.
DependencyResolver.SetResolver(
            new Dependency(RegisterServices(builder))
            ); 

The constructor of the Dependency class has an IContainer parameter. 

public Dependency(IContainer container)
{
    this.container = container;
}

Autofac.IContainer: Creates, wires dependency and manages lifetime for a set of components. Most instances of Autofac.IContainer are created by Autofac.ContainerBuilder.

And at last the RegisterService method as follows which returns an IContainer and takes ContainerBuilder as a parameter.

private static IContainer RegisterServices(ContainerBuilder builder)
{

    builder.RegisterAssemblyTypes(
        typeof(MvcApplication).Assembly
        ).PropertiesAutowired();

    //deal with your dependencies here
    builder.RegisterType<DataContext>().As<IDbContext>().InstancePerDependency();

    builder.RegisterGeneric(typeof(RepositoryService<>)).As(typeof(IRepository<>));

    builder.RegisterType<ProductService>().As<IProductService>();
    builder.RegisterType<CategoryService>().As<ICategoryService>();


    return
        builder.Build();
}

In the above method we shall add all of our services for registration.

At this moment we shall call the DependencyConfigure class for initialization in the Global.asax file.

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

Now create a Category controller as below:

public class CategoryController : Controller
{
    private ICategoryService categoryService;
    public CategoryController(ICategoryService categoryService)
    {
        this.categoryService = categoryService;
    }
    //
    // GET: /Category/

    public ActionResult Index()
    {
        var cagetories = categoryService.GetAll().ToList();

        var categoryModelList =
            cagetories.Select(p =>
            {
                var categoryModel = new CategoryModel();
                categoryModel.Id = p.Id;
                categoryModel.Name = p.Name;

                return categoryModel;
            }
                );
        return View(categoryModelList);
    }

    //
    // GET: /Category/Details/5

    public ActionResult Details(int id)
    {
        var category = categoryService.GetById(id);
        var categoryModel = new CategoryModel()
        {
            Id = category.Id,
            Name = category.Name
        };
        return View(categoryModel);
    }

    //
    // GET: /Category/Create

    public ActionResult Create()
    {
        var model = new CategoryModel();
        return View(model);
    } 

    //
    // POST: /Category/Create

    [HttpPost]
    public ActionResult Create(CategoryModel model)//FormCollection collection
    {
        try
        {
            // TODO: Add insert logic here
            if (model == null)
                return View(model);

            var category = new Category();
            category.Name = model.Name;

            categoryService.Insert(category);

            return RedirectToAction("Index");
        }
        catch
        {
            return View(model);
        }
    }
        
    //
    // GET: /Category/Edit/5

    public ActionResult Edit(int id)
    {
        var category = categoryService.GetById(id);
        var categoryModel = new CategoryModel()
            {
                Id = category.Id,
                Name = category.Name
            };
        return View(categoryModel);
    }

    //
    // POST: /Category/Edit/5

    [HttpPost]
    public ActionResult Edit(int id, CategoryModel model)
    {
        try
        {
            // TODO: Add update logic here
            if (model == null)
                return View(model);
            var category= categoryService.GetById(id);
            category.Name = model.Name;

            categoryService.Update(category);

            return RedirectToAction("Index");
        }
        catch
        {
            return View(model);
        }
    }

    //
    // GET: /Category/Delete/5

    public ActionResult Delete(int id)
    {
        var category = categoryService.GetById(id);

        var categoryModel = new CategoryModel()
        {
            Id = category.Id,
            Name = category.Name
        };
        return View(categoryModel);

    }

    //
    // POST: /Category/Delete/5

    [HttpPost]
    public ActionResult Delete(int id,  CategoryModel model)
    {
        try
        {
            // TODO: Add delete logic here

            if (id ==0)
                return RedirectToAction("Index");

            var category = categoryService.GetById(id);

            categoryService.Delete(category);

            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }
} 

The Controller's constructor will hold a parameter of the ICategoryService interface.

Before creating a view we shall add two classes in the model folder.

CategoryModel.cs:

public class CategoryModel
{
    public int Id { get; set; }
    public virtual string Name { get; set; }
    public List<ProductModel> Products { get; set; }

}

ProductModel.cs:

public class ProductModel
{
    public Int64 Id { get; set; }
    public int CategoryId { get; set; }
    public CategoryModel Category { get; set; }
    public string Name { get; set; }
    public int MinimumStockLevel { get; set; }
}

Later we can add data validation on these entities.

Now add the necessary Views. Before running the application, modify the connection string in Web.config.

Run the application and go to the Category menu.

The CRUD operations are working.

ProductController isn't completed yet, you may try it yourself. Can anyone of you help me to improve this article? You are most welcome.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here