什么是ASP.NET Core 依赖注入? 依赖注入也称DI是一项技术用来实现对象松耦合以至于应用程序更容易维护,ASP.NET Core通过控制器的构造函数自动注入依赖的对象,我们创建ASP.NET Core MVC应用程序演示依赖注入特性是如何工作, 在这节中我们讲解该特性。
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条数据,在该类中实现了接口中的所有成员
namespace AspNetCore.DependencyInjection.Controllers { public class HomeController : Controller { public IActionResult Index() { return View(new Repository().Products); } } }可以清楚的看到在Index方法中,创建一个Repository实例并且调用了Products属性(new Repository().Products), 这个属性返回字典中存储的所有产品,我们最后将这些数据返回到视图。HomeController以紧耦合方式依赖Repository类,我们使用依赖注入技术实现松耦合。
@{ 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个产品信息
return View(new Repository().Products);假设一段时间后我们需求发生一些变化,我们需要将repository改变为NewRepository,因此我们需要修改控制器中的代码:
public IActionResult Index() { return View(new NewRepository().Products); }这是一个如何管理紧耦合组件的问题,我们为什么避开紧耦合组件?
依赖注入技术解决了这些问题,ASP.NET Core 会为控制器自动提供repository对象,这将移除紧耦合的问题
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件事情
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>()
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类并解析这个类型的依赖,这个依赖注入单个类型,没有任何接口