65.9K
CodeProject 正在变化。 阅读更多。
Home

MVC 仓库模式与 Entity Framework 以及使用 Autofac 解决依赖注入:第一部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (9投票s)

2013 年 3 月 19 日

CPOL

4分钟阅读

viewsIcon

72816

downloadIcon

3528

在这里,我将尝试演示 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.csCategoryService 类相同。

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" 中,我们创建了 DataContextRpositoryService<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.csDependencyConfigure.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 尚未完成,您可以自己尝试。您有人能帮助我改进这篇文章吗?非常欢迎。

© . All rights reserved.