ASP.NET Core MVC依赖注入理解(极简个人版)

依赖注入

文献来源:《Pro ASP.NET Core MVC》 Adam Freeman 第18章 依赖注入

1 依赖注入原理

  • 所有可能变化的地方都用接口
  • 在使用接口的地方用什么实体类通过在ConfigureService中注册解决
  • 注册的实体类需要指定在何种生命周期中有效
    • Transient
    • Scoped
    • Singleton

2 接口

接口只定义契约,不定义实现。

在这里插入图片描述

//IRepository.cs
using System.Collections.Generic;
namespace DependencyInjection.Models{

	public interface IRepository{
	
		IEnumerable<Product> Products{get;}
		
		Product this[string name]{get;}
		
		void AddProduct(Product product);
		
		void DeleteProduct(Product product);
	}
}

//MemoryRepository.cs
using System.Collections.Generic;

namespace DependencyInjection.Models{
    public class MemoryRepository:IRepository{
		private Dictionary<string, Product> products;
	
		public MemoryRepository(){
			products = new Dictionary<string, Product>();
			new List<Product{
				new Product{Name="Kayak", Price=275M},
				new Product{Name="Lifejacket", Price=48.95M},
				new Product{Name="Soccer ball", Price=19.50M},
			}.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);
    }
}

3 数据绑定页面

页面绑定代码使用@model和ViewBag实现。

//Index.cshtml
@using DependencyInjection.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model IEnumerable<Product>
@{layout=null;}

<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>Dependency Injection</title>
        <link rel="stylesheet" asp-href-include="lib/bootstrap/dist/css/*.min.css" />
    </head>
    <body class="panel-body">
        @if(ViewData.Count>0){
            <table class="table table-bordered table-condense table-striped">
                @foreach(var kvp in ViewData){
                    <tr>
                        <td>@kvp.Key</td>
                        <td>@kvp.Value</td>
                    </tr>
                }
            </table>
        }
        <table class="table table-bordered table-condense table-striped">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Price</th>
                </tr>
            </thead>
            <tbody>
                @if(Model==null){
                    <tr><td colspan="3" class="text-center">No Model Data</td></tr>
                }
                else{
                    @foreach(var p in Model){
                        <tr>
                            <td>@p.Name</td>
                            <td>@string.Format("{0:C2}",p.Price)</td>
                        </tr>
                    }
                }
            </tbody>
        </table>
    </body>
</html>

该页面绑定了2组数据集

  • ViewData集合
  • @model IEnumerable<Product>

后台绑定

//HomeController.cs
//使用实体类的Products属性绑定页面
using Microsoft.AspNetCore.Mvc;
using DependencyInjection.Models;

public class HomeController:Controller{
    public ViewResult Index() => View(new MemoryRepository().Products);
}

4 ASP.NET MVC定义依赖注入

4.1 接口依赖注入

  • 单实例依赖注入

首先将后台处理由指定的实体类换成接口

//HomeController.cs
using Microsoft.AspNetCore.Mvc;
using DependencyInjection.Models;
using DependencyInjection.Infrastructure;

namespace DependencyInjection.Controllers{
    //增加接口私有变量
	private IRepository repository;
    
    //通过构造函数传参至该私有变量
	public HomeController(IRepository repo){
		repository = repo;
	}
	
    //绑定接口的Products属性
    //运行时由ASP.NET MVC框架确定使用何种实体类
	public ViewResult Index()=>View(repository.Products);
}

然后配置接口与实体类之间的关系

//Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using DependencyInjection.Infrastructure;
using DependencyInjection.Model;

namespace DependencyInjection{
    public void ConfigureServices(IServicesCollection services){
        services.AddTransient<IRepository, MemoryRepository>();
        ...
    }
    ...
}

services.AddTransient告诉service provider如何处理依赖。这里要注意一点,service provider是整个MVC框架服务的总提供者。

  • 链式依赖

    某实体类依赖于某接口,且该实体类中的成员变量又依赖于另一接口。

在这里插入图片描述

//MemoryRepository.cs
using DependencyInjection.Models{
	public class MemoryRepository:IRepository{
		private IModelStorage storage;//新增私有变量用于指定所属的仓库
		public MemoryStorage(IModelStorage storage){
			storage = modelStorage;
			new List<Product>{
				new Product{Name="Kayak",Price=275M},
				new Product{Name="Lifejacket", Price=48.95M},
				new Product{Name="Soccer ball", Price=19.50M}
			}.ForEach(p=>AddProduct(p));
		}
		
		public IEnumerable<Product> Products=>storage.Items;
		
		public Product this[string name] => storage[name];
		
		public void AddProduct(Product product){
			storage[product.Name] = product;
		}
		
		public void DeleteProduct(Product product){
			storage.RemoveItem(product.Name); 
		}
	}
}

ConfigureServices中注册。

//Startup.cs
...
namespace DependencyInjection{
    public class Startup{
        public void ConfigureServices(IServiceCollection services){
            services.AddTransient<IRepository, MemoryRepository>();
            services.AddTransient<IModelStorage, DictionaryStorage>();
            ...
        }
        ...
    }
}

4.2 实体类依赖注入

假设存在实体类ProductTotalizer用于计算总价。

using System.Linq;
namespace DependencyInjection.Models{
	public class ProductTotalizer{
		public ProductTotalizer(IRepository repo){
			Repository = repo;
		}
		
        //所述仓库用接口表示
		public IRepository Repository {get;set;}
		
        //总价
		public decimal Total => Repository.Products.Sum(p=>p.Price);
	}
}

在这里插入图片描述
HomeController中加入ProductTotalizer变量。

...
public class HomeController:Controller{
	private IRepository repository;
	private ProductTotalizer totalizer;
	
	public HomeController(IRepository repo, ProductTotalizer total){
		repository = repo;
		totalizer = total;
	}
	
	public viewResult Index(){
		ViewBag.Total = totalizer.Total;
		return View(repository.Products);
	}
}

Index.cshtml中包含了对ViewBag的显示,这里实际绑定了2个数据,第一个是ViewBag,第二个是repository

为了明确在Controller中使用何种ProductTotalizer(可能在后面的开发中,ProductTotalizer是父类),可以对其进行强行指定。

//Startup.cs
...
public void ConfigureServices(IServicesCollection services){
	...
	services.AddTransient<ProductTotalizer>();
	...
}

5 服务生命周期

类别生命周期
Transient只要有调用就创建新的对象。就算类型一样,但是对象不一样
Scoped在一个Controller之中,只要类型一样,对象就一样。刷新Controller之后对象变了,但是多个同一类型的对象还是同一个。
Singleton只要初始化了,就一直是这个对象,不管是否刷新Controller

5.1 Transient

首先在service provider中注册IRepository的调用使用MemoryRepository

...
public void ConfigureServices(IServiceCollection services){
	services.AddTransient<IRepository, MemoryRepository>();
	...
}
...

repository中加入ToString()函数,该函数的实现中包含了GUID的生成。

//MemoryRepository.cs
using System.Collections.Generic;
namespace DependencyInjection.Models{
	public class MemoryRepository:IRepository{
        private IModelStorage storage;
        private string guid = System.Guid.NewGuid().ToString();
        
        public MemoryRepository(IModelStorage modelStore){
            storage = modelStore;
            ...
        }
        
        ...
            
        public override string ToString(){
            return guid;
        }
    }
}

在下图中可以看到,当HomeController调用repository时,service provider会针对每一次调用都生成一个新的对象。
在这里插入图片描述

5.2 Scoped

其他的调用都一样,除了在service中注册使用AddScoped之外。

//Startup.cs
...
public void ConfigureServices(IServiceCollection services){
	service.AddScoped<IRepository, MemoryRepository>();
	...
}

在这里插入图片描述

5.3 Singleton

其他的调用都一样,除了在service中注册使用AddSingleton之外。

//Startup.cs
...
public void ConfigureServices(IServiceCollection services){
	services.AddSingleton<IRepository, MemoryRepository>();
	...
}
...

在这里插入图片描述

6 其他依赖注入

6.1 Action依赖注入

上述的依赖注入是针对整个Controller,但有的时候只想针对某一个Action进行依赖注入。此时只需要使用[FromServices]即可。

using Microsoft.AspNetCore.Mvc;
using DependencyInjection.Models;
using DependencyInjection.Infrastructure;

namespace DependencyInjection.Controllers{
	public class HomeController:Controller{
		private IRepository repository;
		
		public HomeController(IRepository repo){
			repository = repo;
		}
		
		public ViewResult Index([FromServices]ProductTotalizer totalizer){
			ViewBag.HomeController = repository.ToString();
			ViewBag.Totalizer = totalizer.Repository.ToString();
			return View(repository.Products);
		}
	}
}

上述代码表示在Index中需要使用ProductTotalizer时,从service provider处获得。

6.2 手工进行依赖注入

除了上述的注册方式外,还有一些其他的注册方式。比如,当没有依赖出现时,而你又想获得一个接口的实例,该实例依赖于接口。在这种情况下,你可以直接通过service provider来实现。

...
public class HomeController:Controller{
	public ViewResult Index([FromServices]ProductTotalizer totalizer){
		IRepository repository=
			HttpContext.RequestServices.GetService<IRepository>();
			...
	}
}
...
  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《Pro ASP.NET Core MVC 2(第7版)中文版》是一本介绍ASP.NET Core MVC 2的权威指南。本书主要针对ASP.NET Core MVC 2的开发者和学习者,提供了全面且深入的内容。 这本书的主要特点包括以下几个方面: 1. 详尽的介绍:本书涵盖了ASP.NET Core MVC 2的所有关键概念和特性,包括路由、模型绑定、控制器、视图、中间件等。读者可以通过本书全面了解ASP.NET Core MVC 2的工作原理和使用方法。 2. 实战案例:除了理论知识,本书还提供了大量的实际案例,帮助读者将所学知识应用于实际开发中。这些案例覆盖了各种常见的应用场景,如用户认证、数据验证、日志记录等,读者可以通过阅读这些案例来掌握如何开发高质量的ASP.NET Core MVC 2应用程序。 3. 最佳实践和高级主题:本书还介绍了许多ASP.NET Core MVC 2的最佳实践和高级主题,包括性能优化、安全性、单元测试等。通过学习这些内容,读者可以进一步提升自己的开发技能,写出更加高效、安全的应用程序。 4. 附带示例代码和练习:为了帮助读者更好地理解书中的概念和技术,本书附带了大量的示例代码和练习。读者可以自己动手实践这些示例和练习,巩固所学知识。 总的来说,《Pro ASP.NET Core MVC 2(第7版)中文版》是一本非常权威且实用的ASP.NET Core MVC 2指南。无论是初学者还是有经验的开发者,都可以通过阅读本书,提升自己在ASP.NET Core MVC 2开发领域的技术水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值