把旧系统迁移到.Net Core 2.0 日记(3) - 详解依赖注入 (转)
关于DI 依赖注入, 转载这篇文章, 写得很好的.
-----------------------------
一、什么是依赖注入
- 1.1 依赖
 - 1.2 什么注入
 - 为什么反转
 - 何为容器
 
二、.NET Core DI
- 2.1 实例的注册
 - 2.2 实例生命周期之单例
 - 2.3 实例生命周期之Tranisent
 - 2.4 实例生命周期之Scoped
 
三、DI在ASP.NET Core中的应用
- 3.1 在Startup类中初始化
 - 3.2 Controller中使用
 - 3.3 View中使用
 - 3.4 通过HttpContext来获取
 
四、如何替换其它的Ioc容器
一、什么是依赖注入(Denpendency Injection)
1.1依赖

1.2 什么是注入
private ILoginService<ApplicationUser> _loginService;
public AccountController()
{
  _loginService = new EFLoginService()
}
大师说,这样不好。你不应该自己创建它,而是应该由你的调用者给你。于是你通过构造函数让外界把这两个依赖传给你。
 public
 AccountController(ILoginService<ApplicationUser> loginService)
{
  _loginService = loginService;
}
把依赖的创建丢给其它人,自己只负责使用,其它人丢给你依赖的这个过程理解为注入。
1.3 为什么要反转?

 var controller = new AccountController(new EFLoginService());
controller.Login(userName, password);
// 用Redis来替换原来的EF登录
var controller = new AccountController(new RedisLoginService());
controller.Login(userName, password);
1.4 何为容器
- 绑定服务与实例之间的关系
 
- 获取实例,并对实例进行管理(创建与销毁)
 

二、.NET Core DI
2.1 实例的注册
- IServiceCollection 负责注册
 
- IServiceProvider 负责提供实例
 
var serviceCollection = new ServiceCollection()
  .AddTransient<ILoginService, EFLoginService>()
  .AddSingleton<ILoginService, EFLoginService>()
  .AddScoped<ILoginService, EFLoginService>();
public interface IServiceCollection : IList<ServiceDescriptor>
{
}
我们上面的AddTransient、AddSignletone和Scoped方法是IServiceCollection的扩展方法, 都是往这个List里面添加ServiceDescriptor。
private static IServiceCollection Add(
  IServiceCollection collection,
  Type serviceType,
  Type implementationType,
  ServiceLifetime lifetime)
{
  var descriptor =
  new ServiceDescriptor(serviceType, implementationType, lifetime);
  collection.Add(descriptor);
  return collection;
}
2.2 实例的生命周期之单例
- Transient: 每一次GetService都会创建一个新的实例
 - Scoped: 在同一个Scope内只初始化一个实例 ,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内)
 - Singleton :整个应用程序生命周期以内只创建一个实例
 
public enum ServiceLifetime
{
  Singleton,
  Scoped,
  Transient
}
public interface IOperation
{
        Guid OperationId { get; }
}
public interface IOperationSingleton : IOperation { }
public interface IOperationTransient : IOperation{}
public interface IOperationScoped : IOperation{}
我们的 Operation实现很简单,可以在构造函数中传入一个Guid进行赋值,如果没有的话则自已New一个 Guid。
public class Operation :
  IOperationSingleton,
  IOperationTransient,
  IOperationScoped
{
    private Guid _guid;
    public Operation() {
        _guid = Guid.NewGuid();
    }
    public Operation(Guid guid)
    {
        _guid = guid;
    }
    public Guid OperationId => _guid;
}
在程序内我们可以多次调用ServiceProvider的GetService方法,获取到的都是同一个实例。
var services = new ServiceCollection();
// 默认构造
services.AddSingleton<IOperationSingleton, Operation>();
// 自定义传入Guid空值
services.AddSingleton<IOperationSingleton>(
  new Operation(Guid.Empty));
// 自定义传入一个New的Guid
services.AddSingleton <IOperationSingleton>(
  new Operation(Guid.NewGuid()));
var provider = services.BuildServiceProvider();
// 输出singletone1的Guid
var singletone1 = provider.GetService<IOperationSingleton>();
Console.WriteLine($"signletone1: {singletone1.OperationId}");
// 输出singletone2的Guid
var singletone2 = provider.GetService<IOperationSingleton>();
Console.WriteLine($"signletone2: {singletone2.OperationId}");
Console.WriteLine($"singletone1 == singletone2 ? : { singletone1 == singletone2 }");

我们对IOperationSingleton注册了三次,最后获取两次,大家要注意到我们获取到的始终都是我们最后一次注册的那个给了一个Guid的实例,前面的会被覆盖。
2.3 实例生命周期之Tranisent
这次我们获取到的IOperationTransient为两个不同的实例。
var services = new ServiceCollection();
services.AddTransient<IOperationTransient, Operation>();
var provider = services.BuildServiceProvider();
var transient1 = provider.GetService<IOperationTransient>();
Console.WriteLine($"transient1: {transient1.OperationId}");
var transient2 = provider.GetService<IOperationTransient>();
Console.WriteLine($"transient2: {transient2.OperationId}");
Console.WriteLine($"transient1 == transient2 ? :
  { transient1 == transient2 }");

2.4 实例生命周期之Scoped
 var services = new ServiceCollection()
.AddScoped<IOperationScoped, Operation>()
.AddTransient<IOperationTransient, Operation>()
.AddSingleton<IOperationSingleton, Operation>();
接下来我们用ServiceProvider.CreateScope方法创建一个Scope
var provider = services.BuildServiceProvider();
using (var scope1 = provider.CreateScope())
{
    var p = scope1.ServiceProvider;
    var scopeobj1 = p.GetService<IOperationScoped>();
    var transient1 = p.GetService<IOperationTransient>();
    var singleton1 = p.GetService<IOperationSingleton>();
    var scopeobj2 = p.GetService<IOperationScoped>();
    var transient2 = p.GetService<IOperationTransient>();
    var singleton2 = p.GetService<IOperationSingleton>();
    Console.WriteLine(
        $"scope1: { scopeobj1.OperationId }," +
        $"transient1: {transient1.OperationId}, " +
        $"singleton1: {singleton1.OperationId}");
    Console.WriteLine($"scope2: { scopeobj2.OperationId }, " +
        $"transient2: {transient2.OperationId}, " +
        $"singleton2: {singleton2.OperationId}");
}
接下来
scope1: 200d1e63-d024-4cd3-88c9-35fdf5c00956,
transient1: fb35f570-713e-43fc-854c-972eed2fae52,
singleton1: da6cf60f-670a-4a86-8fd6-01b635f74225
scope2: 200d1e63-d024-4cd3-88c9-35fdf5c00956,
transient2: 2766a1ee-766f-4116-8a48-3e569de54259,
singleton2: da6cf60f-670a-4a86-8fd6-01b635f74225
如果再创建一个新的Scope运行,
scope1: 29f127a7-baf5-4ab0-b264-fcced11d0729,
transient1: 035d8bfc-c516-44a7-94a5-3720bd39ce57,
singleton1: da6cf60f-670a-4a86-8fd6-01b635f74225
scope2: 29f127a7-baf5-4ab0-b264-fcced11d0729,
transient2: 74c37151-6497-4223-b558-a4ffc1897d57,
singleton2: da6cf60f-670a-4a86-8fd6-01b635f74225

三、DI在ASP.NET Core中的应用
3.1在Startup类中初始化
public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<ILoginService<ApplicationUser>,
      EFLoginService>();
    services.AddMvc();
)
ASP.NET Core的一些组件已经提供了一些实例的绑定,像AddMvc就是Mvc Middleware在 IServiceCollection上添加的扩展方法。
public static IMvcBuilder AddMvc(this IServiceCollection services)
{
    if (services == null)
    {
        throw new ArgumentNullException(nameof(services));
    }
    var builder = services.AddMvcCore();
    builder.AddApiExplorer();
    builder.AddAuthorization();
    AddDefaultFrameworkParts(builder.PartManager);
    ...
}
3.2 Controller中使用
 private ILoginService<ApplicationUser> _loginService;
public AccountController(
  ILoginService<ApplicationUser> loginService)
{
  _loginService = loginService;
}
我们只要在控制器的构造函数里面写了这个参数,ServiceProvider就会帮我们注入进来。这一步是在Mvc初始化控制器的时候完成的,我们后面再介绍到Mvc的时候会往细里讲。
3.3 View中使用
@using MilkStone.Services;
@model MilkStone.Models.AccountViewModel.LoginViewModel
@inject ILoginService<ApplicationUser>  loginService
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head></head>
<body>
  @loginService.GetUserName()
</body>
</html>
3.4 通过 HttpContext来获取实例
HttpContext.RequestServices.GetService<ILoginService<ApplicationUser>>();
四、如何替换其它的Ioc容器
builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
这会给我们的初始化带来一些便利性,我们来看看如何替换Autofac到ASP.NET Core。我们只需要把Startup类里面的 ConfigureService的 返回值从 void改为 IServiceProvider即可。而返回的则是一个AutoServiceProvider。
public IServiceProvider ConfigureServices(
  IServiceCollection services){
    services.AddMvc();
    // Add other framework services
    // Add Autofac
    var containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterModule<DefaultModule>();
    containerBuilder.Populate(services);
    var container = containerBuilder.Build();
    return new AutofacServiceProvider(container);
}
4.1 有何变化

把旧系统迁移到.Net Core 2.0 日记(3) - 详解依赖注入 (转)的更多相关文章
- 把旧系统迁移到.Net Core 2.0 日记 (15) --Session 改用Redis
		
安装Microsoft.Extensions.Caching.Redis.Core NuGet中搜索Microsoft.Extensions.Caching.Redis.Core并安装,此NuGet包 ...
 - 把旧系统迁移到.Net Core 2.0 日记(1) - Startup.cs 解析
		
因为自己到开发电脑转到Mac Air,之前的Webform/MVC应用在Mac 跑不起来,而且.Net Core 2.0 已经比较稳定了. 1. 为什么会有跨平台的.Net Core 近年来,我们已 ...
 - 把旧系统迁移到.Net Core 2.0 日记 (12)  --发布遇到的问题
		
1. 开发时是在Mac+MySql, 尝试发布时是在SQL2005+Win 2008 (第一版) 在Startup.cs里,数据库连接要改,分页时netcore默认是用offset关键字分页, 如果用 ...
 - 把旧系统迁移到.Net Core 2.0 日记(4) - 使用EF+Mysql
		
因为Mac 不能装SqlServer, 所以把数据库迁移到MySql,然后EntityFramework要改成Pomelo.EntityFrameworkCore.MySql 数据库迁移时,nvarc ...
 - 把旧系统迁移到.Net Core 2.0 日记 (17)  --多租户和SoftDelete
		
在EF Core 2.0版本中出现了全局过滤新特性即HasQueryFilter,它出现的意义在哪里?能够解决什么问题呢? 通过HasQueryFilter方法来创建过滤器能够允许我们对访问特定数据库 ...
 - 把旧系统迁移到.Net Core 2.0 日记(10) -- EF core 和之前版本多对多映射区别
		
EF Core 现在不支持多对多映射,只能做2个一对多映射. 比如Product和Category 我现在定义Product和Category是多对多关系. 那么实体定义如下: public clas ...
 - 把旧系统迁移到.Net Core 2.0 日记(2) - 依赖注入/日志NLog
		
Net Core 大量使用依赖注入(Dependency Inject), 打个比方,我们常用的日志组件有Log4Net,NLog等等. 如果我们要随时替换日志组件,那么代码中就不能直接引用某个组件的 ...
 - 把旧系统迁移到.Net Core 2.0 日记 (18)  --JWT 认证(Json Web Token)
		
我们最常用的认证系统是Cookie认证,通常用一般需要人工登录的系统,用户访问授权范围的url时,会自动Redirect到Account/Login,登录后把认证结果存在cookie里. 系统只要找到 ...
 - 把旧系统迁移到.Net Core 2.0 日记(8) - EASYUI datagrid+ Dapper+ 导出Excel
		
迁移也没太大变化,有一个, 之前的Request.QueryString 是返回NameValueCollection, 现在则是返回整个字符串. 你要改成Request.Query[“key”] 直 ...
 
随机推荐
- 理解git的分支原理,更好地使用git
			
文章内容转载于git-scm. 部分内容涉嫌枯燥 一.git分支概念 几乎每一种版本控制系统都以某种形式支持分支.使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作.在很多版本控 ...
 - 理解 Redis(9) - Publish Subscribe 消息订阅
			
在窗口1开通一个名为 redis 的通道: 127.0.0.1:6379> SUBSCRIBE redis Reading messages... (press Ctrl-C to quit) ...
 - 【ATcoder】D - Half Reflector
			
题目链接:http://agc011.contest.atcoder.jp/tasks/agc011_d 每次都是两道题惨啊.... 想了想大概做法,既然小球走过去就会导致装置变化?那么是不是有一点像 ...
 - 关于datatables自适应以及自定义列宽度的总结
			
table-layout:fixed;可以自定义列的宽度 <div id="bizhi" style="width:100%;height: 85%;overflo ...
 - winfrom datagridview ,picturebox,显示图片,以及删除问题
			
private void write_listview(DataSet ds) { DataTable dt = ds.Tables[0]; ...
 - WIN7右键在目录当前打开命令行Cmd窗口
			
Win7系统大家习惯“Win+R”的组合键打开命令提示符. 2. 通常情况下,我们点击鼠标右键是没有命令行选项的.. 3.在桌面上先按住Shift键,然后鼠标右键,出现选项“在此处打开命令窗口(W)” ...
 - Python 编程快速上手 第五章总结
			
第五章 字典和结构化数据 创建数组 格式:myCat = {'size':'fat','color':'gray',disposition':'loud'} 对字典的操作 通过[ ] 访问字典的值 [ ...
 - springBoot springSecurty:  x-frame-options deny禁止iframe调用
			
springBoot springSecurty: x-frame-options deny禁止iframe调用 https://blog.csdn.net/whiteforever/article ...
 - English Voice of  <<See You Again >>
			
<See You Again >(<当我们再相见>) 演唱:Wiz Khalifa/Charlie Puth 维兹·卡利法/查理·普斯 It's been a long da ...
 - LeetCode--026--删除排序数组中的重复项
			
问题描述: 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成 ...