第四节:配置的读取、StartUp类、内置依赖注入和扩展改造
一. 配置的读取
在Asp.Net Core中,有一个 appsettings.json 文件,用于存储相应的配置信息,读取的时,要通过构造函数注入:IConfiguration Configuration,来进行读取。
下面是一段配置文件,我们来对其进行读取:
读取代码:
1 public IConfiguration Configuration { get; } 2 public FirstController(IConfiguration configuration) 3 { 4 Configuration = configuration; 5 } 6 { 7 var f0 = Configuration["MyFullName"]; 8 var f1 = Configuration["User:userName"]; 9 var f2 = Configuration["User:Child:childName"]; 10 var f3 = Configuration["StudentList:0:sName"]; 11 var f4 = Configuration["StudentList:1:sName"]; 12 }
二. StartUp类
1. 说明:
StartUp类中包括两个方法,分别是ConfigureServices和Configure,前者主要用来注册服务,后者主要用来创建和配置请 求管道,然后在Main方法中进行调用:
WebHost.CreateDefaultBuilder(args).UseStartup<Startup>();
2. ConfigureServices方法
(1). 它在Configure方法调用之前,由主机调用。
(2). 需要大量设置的功能,IServiceCollection 上有 Add{Service} 扩展方法,常见的如下:
a:services.AddDbContext<ApplicationDbContext>(options =>options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection")));
b:services.AddDefaultIdentity<IdentityUser>().AddDefaultUI(UIFramework.Bootstrap4).AddEntityFrameworkStores<ApplicationDbContext>();
c:services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
3. Configure方法
(1).用于配置Http请求管道,主要通过IApplicationBuilder实例来配置。
(2).常见的管道有:
开发人员异常页(UseDeveloperExceptionPage)、
HTTP严格传输安全性(UseHsts)、
HTTPS重定向(UseHttpsRedirection)、
静态文件(UseStaticFiles)、
MVC(UseMvc)。
三. 内置依赖注入和扩展改造
1. 说明:
这里不再介绍什么是IOC和DI,直接介绍内置依赖注入的使用方法和生命周期,最后介绍利用AutoFac进行替换内置IOC容器。事先准备好U1-U4四个类和IU1-IU4四个接口。
接口和类的代码:
1 public interface IU1 2 { 3 string guid { get; set; } 4 string GetName(); 5 } 6 public interface IU2 7 { 8 string guid { get; set; } 9 string GetName(); 10 } 11 public interface IU3 12 { 13 string guid { get; set; } 14 string GetName(); 15 } 16 public interface IU4 17 { 18 string guid { get; set; } 19 string GetName(); 20 }
1 public class U1 : IU1 2 { 3 public string guid { get; set; } 4 5 public U1() 6 { 7 guid = System.Guid.NewGuid().ToString("N"); 8 } 9 10 /// <summary> 11 /// 调用方法的时候,是单例也没有用,每次调用都会变 12 /// </summary> 13 /// <returns></returns> 14 public string GetName() 15 { 16 return System.Guid.NewGuid().ToString("N"); 17 } 18 } 19 public class U2 : IU2 20 { 21 public string guid { get; set; } 22 23 public U2() 24 { 25 guid = System.Guid.NewGuid().ToString("N"); 26 } 27 28 /// <summary> 29 /// 调用方法的时候,是单例也没有用,每次调用都会变 30 /// </summary> 31 /// <returns></returns> 32 public string GetName() 33 { 34 return System.Guid.NewGuid().ToString("N"); 35 } 36 } 37 public class U3 : IU3 38 { 39 public string guid { get; set; } 40 41 public U3() 42 { 43 guid = System.Guid.NewGuid().ToString("N"); 44 } 45 46 /// <summary> 47 /// 调用方法的时候,是单例也没有用,每次调用都会变 48 /// </summary> 49 /// <returns></returns> 50 public string GetName() 51 { 52 return System.Guid.NewGuid().ToString("N"); 53 } 54 } 55 public class U4 : IU4 56 { 57 public string guid { get; set; } 58 59 public U4() 60 { 61 guid = System.Guid.NewGuid().ToString("N"); 62 } 63 64 /// <summary> 65 /// 调用方法的时候,是单例也没有用,每次调用都会变 66 /// </summary> 67 /// <returns></returns> 68 public string GetName() 69 { 70 return System.Guid.NewGuid().ToString("N"); 71 } 72 }
2. 核心:
利用IServiceCollection services这个对象进行注册,而这个对象在Startup类中的ConfigureServices中已经注入进去了, 也就是说我在自定义对象的注册是的时候,可以直接在ConfigureServices类中进行注册,然后在控制器中使用的时候通过构造函数进行注入即可。
PS:我们也可以自己 IServiceCollection container = new ServiceCollection(); 然后进行注册(不推荐,特殊情况下可能这么用)
3. 生命周期分为三种:
瞬时的(AddTransient)、请求内单例(AddScoped)、单例(AddSingleton 两种写法)。
4. 测试:
两次访问该页面,发现ViewBag.r1 和 ViewBag.r2两次的值不同,ViewBag.r3和ViewBag.r4两次的值相同。
代码分享:
1 public void ConfigureServices(IServiceCollection services) 2 { 3 services.Configure<CookiePolicyOptions>(options => 4 { 5 // This lambda determines whether user consent for non-essential cookies is needed for a given request. 6 options.CheckConsentNeeded = context => true; 7 options.MinimumSameSitePolicy = SameSiteMode.None; 8 }); 9 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); 10 11 //自定义注册对象 12 { 13 services.AddTransient<IU1, U1>(); //瞬时的 14 services.AddScoped<IU2, U2>(); //一次请求内是不变的 15 services.AddSingleton<IU3, U3>(); //单例的 16 services.AddSingleton<IU4>(new U4()); //单例的 17 18 } 19 }
1 public class FirstController : Controller 2 { 3 public IU1 U1 { get; } 4 public IU1 U11 { get; } 5 public IU2 U2 { get; } 6 public IU2 U22 { get; } 7 public IU3 U3 { get; } 8 public IU3 U33 { get; } 9 public IU4 U4 { get; } 10 public IU4 U44 { get; } 11 12 public FirstController(IU1 u1, IU1 u11, IU2 u2, IU2 u22, IU3 u3, IU3 u33, IU4 u4, IU4 u44) 13 { 14 U1 = u1; 15 U11 = u11; 16 U2 = u2; 17 U22 = u22; 18 U3 = u3; 19 U33 = u33; 20 U4 = u4; 21 U44 = u44; 22 } 23 24 public IActionResult Index() 25 { 26 bool a = object.ReferenceEquals(U1, U11); 27 bool b = object.ReferenceEquals(U2, U22); 28 bool c = object.ReferenceEquals(U3, U33); 29 bool d = object.ReferenceEquals(U4, U44); 30 31 //分两次请求来查看 32 ViewBag.r1 = U1.guid; 33 ViewBag.r2 = U2.guid; 34 ViewBag.r3 = U3.guid; 35 ViewBag.r4 = U4.guid; 36 37 //下面是自己创建的的模式进行访问 38 { 39 IServiceCollection container = new ServiceCollection(); 40 container.AddTransient<IU2, U2>(); 41 //要放在注册的后面 42 var provider = container.BuildServiceProvider(); 43 IU2 user1 = provider.GetService<IU2>(); 44 IU2 user2 = provider.GetService<IU2>(); 45 bool cc = object.ReferenceEquals(user1, user2);47 } 48 return View(); 49 } 50 }
1 <div class="text-center"> 2 <h1 class="display-4">Welcome</h1> 3 <p>瞬时的:@ViewBag.r1</p> 4 <p>请求内单例:@ViewBag.r2</p> 5 <p>全局单例写法1:@ViewBag.r3</p> 6 <p>全局单例写法2:@ViewBag.r4</p> 7 </div>
补充:
如果不想通过构造函数注入,则可以通过[FromServices]特性来给某个特定方法注入:
如: public void Test(【FromServices】IU1 u1){ }。
两次访问的请求结果
5. 为什么要用AutoFac进行替换内置容器?
因为某些特定的功能内置的容器不支持,比如:属性的注入、基于名称的注入、子容器、自定生存期管理、对迟缓初始化的 Func<T> 支持
6. 用AutoFac替换的步骤
(1). 通过Nuget安装AutoFac【4.9.2】和Autofac.Extensions.DependencyInjection【4.4.0】
(2). 改造ConfigureServices中配置容器并返回IServiceProvider,配置代码详见具体类,注入的代码封装到DefaultModule。
1 public IServiceProvider ConfigureServices(IServiceCollection services) 2 { 3 services.Configure<CookiePolicyOptions>(options => 4 { 5 // This lambda determines whether user consent for non-essential cookies is needed for a given request. 6 options.CheckConsentNeeded = context => true; 7 options.MinimumSameSitePolicy = SameSiteMode.None; 8 }); 9 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); 10 11 // Add Autofac 12 var containerBuilder = new ContainerBuilder(); 13 containerBuilder.RegisterModule<DefaultModule>(); 14 containerBuilder.Populate(services); 15 var container = containerBuilder.Build(); 16 return new AutofacServiceProvider(container); 17 }
DefaultModule代码分享
特别注意:AutoFac的生命周期也有:瞬时的、单次请求内单例的、全局单例的。
1 public class DefaultModule: Module 2 { 3 protected override void Load(ContainerBuilder builder) 4 { 5 //这里就是AutoFac的注入方式,下面采用常规的方式 6 //详见:https://www.cnblogs.com/yaopengfei/p/9479268.html 7 //官网:https://autofac.org/ 8 9 //特别注意:其中很大的一个变化在于,Autofac 原来的一个生命周期InstancePerRequest,将不再有效。正如我们前面所说的,整个request的生命周期被ASP.NET Core管理了, 10 //所以Autofac的这个将不再有效。我们可以使用 InstancePerLifetimeScope ,同样是有用的,对应了我们ASP.NET Core DI 里面的Scoped。 11 12 //瞬时请求(省略InstancePerDependency 也为瞬时) 13 builder.RegisterType<U1>().As<IU1>().InstancePerDependency(); 14 //单次请求内单例 15 builder.RegisterType<U2>().As<IU2>().InstancePerLifetimeScope(); 16 //全局单例 17 builder.RegisterType<U3>().As<IU3>().SingleInstance(); 18 builder.RegisterType<U4>().As<IU4>().SingleInstance(); 19 20 } 21 }
(3). 其他都不变,可以继续沿用构造函数注入。
参考文章:
https://www.cnblogs.com/yaopengfei/p/9479268.html (.Net 平台下的AutoFac的用法)
https://www.cnblogs.com/jesse2013/p/di-in-aspnetcore.html
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。