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);
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();
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();
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;
}
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);
}
public ActionResult Details(int id)
{
var category = categoryService.GetById(id);
var categoryModel = new CategoryModel()
{
Id = category.Id,
Name = category.Name
};
return View(categoryModel);
}
public ActionResult Create()
{
var model = new CategoryModel();
return View(model);
}
[HttpPost]
public ActionResult Create(CategoryModel model) {
try
{
if (model == null)
return View(model);
var category = new Category();
category.Name = model.Name;
categoryService.Insert(category);
return RedirectToAction("Index");
}
catch
{
return View(model);
}
}
public ActionResult Edit(int id)
{
var category = categoryService.GetById(id);
var categoryModel = new CategoryModel()
{
Id = category.Id,
Name = category.Name
};
return View(categoryModel);
}
[HttpPost]
public ActionResult Edit(int id, CategoryModel model)
{
try
{
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);
}
}
public ActionResult Delete(int id)
{
var category = categoryService.GetById(id);
var categoryModel = new CategoryModel()
{
Id = category.Id,
Name = category.Name
};
return View(categoryModel);
}
[HttpPost]
public ActionResult Delete(int id, CategoryModel model)
{
try
{
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.