MVC 仓库模式与 Entity Framework 以及使用 Autofac 解决依赖注入:第一部分
在这里,我将尝试演示 Autofac 在依赖注入和 Entity Framework 用于与存储交互方面的基本配置。
引言
- 在我第一篇博文 "使用 EntityTypeConfiguration 的 Entity Framework 存储库模式" 中,我尝试介绍了如何将存储库模式与 EntityFramework 集成。
- 在这里,我将重点介绍如何在 ASP.NET MVC 3 中使用 Autofac 库处理依赖注入。
- 在这一部分,我将重点介绍容器和 Autofac 配置。但不会涵盖 InstanceScope。我将在下一篇文章中尝试介绍它。
请查看我之前的文章 "使用 EntityTypeConfiguration 的 Entity Framework 存储库模式",这将有助于内容的连续性。
背景
我不想详细介绍 Autofac 的依赖注入,因为有一篇由 Nicholas Blumhardt 撰写的非常出色的文章 在这里 供初学者阅读。它对于理解为什么需要依赖注入非常有帮助。IoC 是一个与依赖注入非常流行的术语。
完成此练习后,我们至少能够开始转向使用 Autofac 进行依赖注入。所以这是我们的起点。
与之前的示例一样,我将尝试使文章简洁,以便读者能够轻松理解。
完成此项后
我们将能够使用 Autofac 配置依赖注入。
所需开发工具
- Visual Studio: 2010 和 C# 语言
- SQL Server: 2008。Express 版本可能也可以。
- MVC-3
- Autofac 库
- Autofac 集成 MVC 库
- Entity Framework 库,因为我们将使用存储库模式存储数据
使用代码
我们在 使用 EntityTypeConfiguration 的 Entity Framework 存储库模式 文章中已经创建了一个存储库。我们将在这里使用相同的存储库模式,并且为了简单起见,只处理 Category 和 Product 这两个类。
现在需要一个服务层,以防止我们在实现范围内直接使用存储库。该层将处理有关存储和业务逻辑的所有类型的操作。
我们的解决方案树将如下所示
- 两个新项目:“EfRepPatTest.Service”作为一个类库,以及“Web.Implementation”作为 MVC3 Web 应用程序。
- 删除上一个项目中的“EfRepPatTest.Implementation”。
源代码已在本文顶部提供。
EfRepPatTest.Service 包含 Category 和 Product 的两个类和两个接口。我们必须在这里添加对 EfRepPatTest.Data 和 EfRepPatTest.Entity 的引用。
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 将使用此接口。
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 与 CategoryService
类相同。
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);
}
}
首先,为什么需要依赖注入?在 "使用 Entity Framework 的存储库模式" 的项目 "EfRepPatTest.Implementation>Program.cs" 中,我们创建了 DataContext
、RpositoryService<Category>
等实例。请看以下代码片段
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"
};
当我们处理大量类并需要每次创建实例来相互使用它们时,每次创建实例都意味着依赖关系将手动处理。
回看 CategoryService
类的构造函数。
public class CategoryService:ICategoryService
{
private IRepository<Category> categroyRepository;
public CategoryService(IRepository<Category> categroyRepository)
{
this.categroyRepository = categroyRepository;
}
调用 CategoryService
时,我们必须提供一个参数,请记住这一点。
让我们看看 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
依赖于 ProductService
类来提供产品信息。
Autofac 将帮助我们解决依赖问题。有几个轻量级库可以做到这一点。
Web.Implementation MVC 项目
首先,在 MVC 项目的 Infrastructure 文件夹中创建两个类,分别命名为 Dependency.cs 和 DependencyConfigure.cs。这些类包含依赖注入的配置。
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();
}
}
Infrastructure 文件夹中的 Dependency.cs 类将实现 System.Web.Mvc
命名空间下的 IDependencyResolver
接口。
ContainerBuilder:用于从组件注册构建 Autofac.IContainer。
var builder = new ContainerBuilder();
- DependencyResolver:为实现
System.Web.Mvc.IDependencyResolver
接口的依赖解析器提供注册点。 - IDependencyResolver:定义了简化服务定位和依赖解析的方法。
- 在这里,
Dependency
类实现了IDependencyResolver
接口。 - DependencyResolver.SetResolver:提供一个依赖解析器的注册点,使用指定的依赖解析器接口。这里的“指定的依赖解析器接口”指的是作为参数的
Dependency
类。
DependencyResolver.SetResolver(
new Dependency(RegisterServices(builder))
);
Dependency
类的构造函数有一个 IContainer
参数。
public Dependency(IContainer container)
{
this.container = container;
}
Autofac.IContainer:创建、连接依赖并管理一组组件的生命周期。大多数 Autofac.IContainer
实例由 Autofac.ContainerBuilder
创建。
最后是 RegisterService
方法,它返回一个 IContainer
并接受 ContainerBuilder
作为参数。
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();
}
在上面的方法中,我们将添加所有服务进行注册。
此时,我们将在 Global.asax 文件中调用 DependencyConfigure
类进行初始化。
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
DependencyConfigure.Initialize();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
现在,像下面这样创建一个 **Category** 控制器
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();
}
}
}
控制器的构造函数将包含一个 ICategoryService
接口的参数。
在创建视图之前,我们将在模型文件夹中添加两个类。
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; }
}
稍后我们可以为这些实体添加数据验证。
现在添加必要的视图。在运行应用程序之前,请修改 Web.config 中的连接字符串。
运行应用程序并转到 Category 菜单。
CRUD 操作正在工作。
ProductController
尚未完成,您可以自己尝试。您有人能帮助我改进这篇文章吗?非常欢迎。