• .NET Core 实现依赖注入案例讲解
  • 发布于 2个月前
  • 230 热度
    0 评论

什么是ASP.NET Core 依赖注入? 依赖注入也称DI是一项技术用来实现对象松耦合以至于应用程序更容易维护,ASP.NET Core通过控制器的构造函数自动注入依赖的对象,我们创建ASP.NET Core MVC应用程序演示依赖注入特性是如何工作, 在这节中我们讲解该特性。


一 例子
我们创建一个ASP.NET Core MVC的应用程序。
1.1 Repository
新建一个Product实体类,为了简单我们将数据存储在应用程序的内存中而不是数据库中,在Models中添加Products类,代码如下
namespace AspNetCore.DependencyInjection.Models
{
    public class Product
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}
接下来,在Models文件夹下创建一个接口名字为IRepository,这个接口中包含了最基本的方法新增,读取和删除产品,接口的代码如下:
namespace AspNetCore.DependencyInjection.Models
{
    public interface IRepository
    {
        // 堆代码 duidaima.com
        IEnumerable<Product> Products { get; }
        Product this[string name] { get; }
        void AddProduct(Product product);
        void DeleteProduct(Product product);
    }
}
现在我们在Models文件夹下创建一个Repository类继承自IRepository接口
namespace AspNetCore.DependencyInjection.Models
{
    public class Repository : IRepository
    {
        private Dictionary<string, Product> products;
        public Repository()
        {
            products = new Dictionary<string, Product>();
            new List<Product> {
                new Product { Name = "Women Shoes", Price = 99M },
                new Product { Name = "Skirts", Price = 29.99M },
                new Product { Name = "Pants", Price = 40.5M }
            }.ForEach(p => AddProduct(p));
        }
        public IEnumerable<Product> Products => products.Values;
        public Product this[string name] => products[name];
        public void AddProduct(Product product) => products[product.Name] = product;
        public void DeleteProduct(Product product) => products.Remove(product.Name);
    }
}
我们在Dictionary中创建了3条数据,在该类中实现了接口中的所有成员
1.2 Controller & Views
我们修改一下HomeController.cs文件将我们的产品显示在视图上,因此更新Index方法
namespace AspNetCore.DependencyInjection.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View(new Repository().Products);
        }
    }
}
可以清楚的看到在Index方法中,创建一个Repository实例并且调用了Products属性(new Repository().Products), 这个属性返回字典中存储的所有产品,我们最后将这些数据返回到视图。HomeController以紧耦合方式依赖Repository类,我们使用依赖注入技术实现松耦合。

我们现在更新一下Views->Home的Index视图:
@{
    ViewData["Title"] = "Home Page";
}
@model IEnumerable<Product>
<div class="row mb-3">
    <div class="col-sm">
        <table class="table table-bordered align-middle">
            <thead>
                <tr>
                    <th>名称</th>
                    <th>价格</th>
                </tr>
            </thead>
            <tbody>
                @foreach (var product in Model)
                {
                    <tr>
                        <td>@product.Name</td>
                        <td>@string.Format("{0:C2}",product.Price)</td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
</div>
这个视图通过循环Model中的数据读取产品信息,显示所有的产品在表格中,运行应用程序我们将看到3个产品信息

二. 使用DI来解决紧耦合
如果你看到控制器中代码你将发现紧耦合Repository.cs类,原因是我们直接创建了一个Repository对象使用下面代码:
return View(new Repository().Products);
假设一段时间后我们需求发生一些变化,我们需要将repository改变为NewRepository,因此我们需要修改控制器中的代码:
public IActionResult Index()
{
    return View(new NewRepository().Products);
}
这是一个如何管理紧耦合组件的问题,我们为什么避开紧耦合组件?
1 它给项目维护带来了很多问题,如果一个组件改变会影响另外一个组件
2 对这些组件进行单元测试时会出现很多问题

依赖注入技术解决了这些问题,ASP.NET Core 会为控制器自动提供repository对象,这将移除紧耦合的问题


三. 在控制器中实现依赖注入
如何在ASP.NET Core中实现依赖注入? 我们可以通过注册服务来解决这个问题,ASP.NET Core DI将解析依赖,在ASP.NET Core 控制器中2个步骤可以实现DI
1 移除控制器中紧耦合,添加新松耦合的依赖
2 新的依赖在ASP.NET Core 中如何解析
首先我们创建一个松耦合的依赖在控制器中,这个我们将实现一个接口并且在控制器中使用这个接口,更新一下HomeController代码因此使用这个接口来创建依赖,代码如下:
namespace AspNetCore.DependencyInjection.Controllers
{
    public class HomeController : Controller
    {
        private IRepository _repository;
        public HomeController(IRepository repository)
        {
            _repository= repository;
        }
        public IActionResult Index()
        {
            return View(new Repository().Products);
        }
    }
}
我们在控制器中做了2件事情
1 在控制器中添加接口IRepository
2 添加了一个构造函数,构造函数的参数是接口类型,在构造函数中我们使用构造函数参数设置接口变量的值
使用这个我们可以添加松耦合依赖在构造函数,现在你的action方法可以访问IRepository接口的方法
最后我们需要告诉ASP.NET Core 如何解析IRepository接口依赖,进入应用程序的Program.cs文件并且为DI容器注册新的服务,我们将通过下面代码完成这个工作
using AspNetCore.DependencyInjection.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<IRepository, Repository>();
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
现在运行应用程序,你将会看到产品显示在页面上,这里我们使用了DI将控制器和Repository类解耦,假设在一段时间后我们需要从另外一个类NewRepository.cs展示产品,你只需要修改一下下面代码就可以了
builder.Services.AddTransient<IRepository, NewRepository>()

四.依赖注入针对单个类型
如果你有一个简单的类没有实现任何接口,该类是一个简单类型,让我们了解如何在这个例子中使用DI,在Models文件夹中创建一个新的类叫ProductSum.cs并添加如下代码
namespace AspNetCore.DependencyInjection.Models
{
    public class ProductSum
    {
        public IRepository Repository { get; set; }
        public ProductSum(IRepository repo)
        {
            Repository = repo;
        }
        public decimal Total => Repository.Products.Sum(p => p.Price);
    }
}
注意这个类没有实现任何接口,在构造函数中指定一个IRepository依赖,有一个Total的属性,返回Repository类所有产品的总和,这个类依赖IRepository接口通过ServiceProvider来解析,我们在之前已经做了配置。在HomeController的构造函数中创建一个ProductSum类的依赖,并且设置一个ViewBag变量包含所有产品的总和,这个ViewBag值将显示在视图,更新HomeController的代码:
namespace AspNetCore.DependencyInjection.Controllers
{
    public class HomeController : Controller
    {
        private IRepository _repository;
        private ProductSum _productSum;
        public HomeController(IRepository repository, ProductSum productSum)
        {
            _repository = repository;
            _productSum = productSum;
        }
        public IActionResult Index()
        {
            return View(new Repository().Products);
        }
    }
}
在Program类中添加下面代码:
using AspNetCore.DependencyInjection.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<IRepository, Repository>();
builder.Services.AddTransient<ProductSum>();
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
在这里我们使用了AddTransient()只有一个参数,以这种方式告诉ServiceProvider初始化ProductSum类并解析这个类型的依赖,这个依赖注入单个类型,没有任何接口

用户评论