关于 项目中Ioc基础模块 的搭建 (已适用于.net core / .net Framework / Nancy)
Ioc?(Inversion of Control, 控制反轉(zhuǎn))把創(chuàng)建對(duì)象的操作交給框架,亦被稱為?DI(Dependency Injection, 依賴注入)。
為什么叫做 “控制反轉(zhuǎn)” 呢?之前,我們想要一個(gè)對(duì)象都是?new?出來的,天天需要?new?對(duì)象是不是感覺有點(diǎn)麻煩。有人就想到了,把這些簡(jiǎn)單重復(fù)的工作也交給框架做。本來需要我們向框架 “射入” 對(duì)象,現(xiàn)在框架自己能產(chǎn)生對(duì)象了,這不正是?控制反轉(zhuǎn)?嗎?于是,就有了這個(gè)響亮的名字。
本文不做具體概念講解 ,項(xiàng)目采用Autofac作為基礎(chǔ)框架
關(guān)于Autofac的基礎(chǔ)用法可以參照官方的文檔教程 很詳細(xì)? 很具體 針對(duì)各種版本都有說明? (不要去看各種入門教程 或者翻譯文檔 全是瞎扯淡) https://autofaccn.readthedocs.io/zh/latest/
?
熟悉Ioc的都應(yīng)該很清楚? 我們常用的操作主要就是兩個(gè)Resolver 和 Registrar
Registrar:
隨著項(xiàng)目的逐漸增大,我們基本都采用模塊化的方式即?Module? Autofac已經(jīng)提供了一個(gè)基礎(chǔ)的Module 我們可以在其內(nèi)部里面重寫Load方法即可,但是考慮以后可能還需要做其他擴(kuò)展所以我們還是提供一個(gè)IRegistrar?接口備用?
參照Load方法 我們只提供一個(gè)?ContainerBuilder
protected override void Load(ContainerBuilder builder)Resolver:
Autofac已經(jīng)幫我們實(shí)現(xiàn)很多場(chǎng)景下的自動(dòng)Resolver,但是具體的業(yè)務(wù)情況卻是我們可能需要在自己任意想要的地方去Resolver? ?所以我們需要自己來實(shí)現(xiàn)個(gè)IResolver
1 public interface IResolver 2 { 3 /// <summary> 4 /// Resolves this instance. 5 /// </summary> 6 /// <typeparam name="T"></typeparam> 7 /// <returns></returns> 8 T Resolve<T>(ILifetimeScope scope = null); 9 10 /// <summary> 11 /// Determines whether this instance is registered. 12 /// </summary> 13 /// <typeparam name="T"></typeparam> 14 /// <returns> 15 /// <c>true</c> if this instance is registered; otherwise, <c>false</c>. 16 /// </returns> 17 bool IsRegistered<T>() where T : class; 18 19 /// <summary> 20 /// Determines whether the specified type is registered. 21 /// </summary> 22 /// <param name="type">The type.</param> 23 /// <param name="scope">The ILifetimeScope</param> 24 /// <returns> 25 /// <c>true</c> if the specified type is registered; otherwise, <c>false</c>. 26 /// </returns> 27 bool IsRegistered(Type type, ILifetimeScope scope = null); 28 29 /// <summary> 30 /// Releases a pre-resolved object. See Resolve methods. 31 /// </summary> 32 /// <param name="obj">Object to be released</param> 33 void Release(object obj); 34 35 /// <summary> 36 /// Resolve 37 /// </summary> 38 /// <typeparam name="T"></typeparam> 39 /// <param name="parameters"></param> 40 /// <param name="scope"></param> 41 /// <returns></returns> 42 T Resolve<T>(IEnumerable<Parameter> parameters, ILifetimeScope scope = null); 43 44 /// <summary> 45 /// Resolve 46 /// </summary> 47 /// <typeparam name="T"></typeparam> 48 /// <param name="parameters"></param> 49 /// <returns></returns> 50 T ResolveParameter<T>(params Parameter[] parameters); 51 52 /// <summary> 53 /// Resolve 54 /// </summary> 55 /// <typeparam name="T"></typeparam> 56 /// <returns></returns> 57 T ResolveName<T>(string name); 58 59 /// <summary> 60 /// Resolve 61 /// </summary> 62 /// <returns></returns> 63 object Resolve(Type type); 64 } View Code?-------------------------------------------------------------------------------------------------------------------------------------
然后 在.net core 中已經(jīng)內(nèi)置了Ioc? 基本代碼如下
1 /// <summary> 2 /// ConfigureServices 3 /// </summary> 4 /// <param name="services"></param> 5 /// <returns></returns> 6 public IServiceProvider ConfigureServices(IServiceCollection services) 7 { 8 } View Code我們可以知道? .net core 內(nèi)置的Ioc? 是以?IServiceCollection 為核心,所以 如果我們需要支持.net core版本則需要??IServiceCollection? ?所以我們提供一個(gè)?IServiceCollectionResolve
?IServiceCollectionResolve
1 public interface IServiceCollectionResolve 2 { 3 IServiceCollection ServiceCollection { get; set; } 4 5 T ResolveServiceValue<T>() where T : class, new(); 6 } View CodeAutofac 的核心在于?IContainer?所以我們提供一個(gè)?IIocManager?
IIocManager?
1 public interface IIocManager : IResolver, IRegistrar, IServiceCollectionResolve 2 { 3 /// <summary> 4 /// Reference to the Autofac Container. 5 /// </summary> 6 IContainer IocContainer { get; set; } 7 8 /// <summary> 9 /// ServiceLocatorCurrent 10 /// </summary> 11 IServiceLocator ServiceLocatorCurrent { get; set; } 12 13 /// <summary> 14 /// SetContainer 15 /// </summary> 16 /// <param name="containerBuilder"></param> 17 void SetContainer(ContainerBuilder containerBuilder); 18 19 /// <summary> 20 /// SetServiceCollection 21 /// </summary> 22 /// <param name="serviceCollection"></param> 23 void SetServiceCollection(IServiceCollection serviceCollection); 24 25 /// <summary> 26 /// UpdateContainer 27 /// </summary> 28 /// <param name="containerBuilder"></param> 29 [Obsolete("Containers should generally be considered immutable. Register all of your dependencies before building/resolving. If you need to change the contents of a container, you technically should rebuild the container. This method may be removed in a future major release.")] 30 void UpdateContainer(ContainerBuilder containerBuilder); 31 } View Code說明:
IServiceLocator 來源于?CommonServiceLocator? 可以在?nuget?找到? 可以理解為IResolver??
UpdateContainer 官方已經(jīng)不推薦使用? 可以使用但是盡量避免使用? 主要適用場(chǎng)景是 :已經(jīng)初始化完成后? 再需要進(jìn)行二次注冊(cè)等操作
?
至此我們所需要的接口基本定義完成。
我們需要一個(gè)實(shí)現(xiàn) 即?IocManager
1 /// <summary> 2 /// IocManager 3 /// </summary> 4 public class IocManager : IIocManager 5 { 6 /// <summary> 7 /// The Singleton instance. 8 /// </summary> 9 public static IocManager Instance { get; } 10 11 #region ContainerBuilder 12 13 /// <summary> 14 /// ContainerBuilder 15 /// </summary> 16 ContainerBuilder IRegistrar.ContainerBuilder 17 { 18 get => ContainerBuilder; 19 set => ContainerBuilder = value; 20 } 21 22 /// <summary> 23 /// ContainerBuilder 24 /// </summary> 25 public static ContainerBuilder ContainerBuilder { get; set; } 26 27 #endregion 28 29 #region IContainer 30 31 /// <summary> 32 /// IocContainer 33 /// </summary> 34 IContainer IIocManager.IocContainer 35 { 36 get => IocContainer; 37 set => IocContainer = value; 38 } 39 40 /// <summary> 41 /// IocContainer 42 /// </summary> 43 public static IContainer IocContainer { get; set; } 44 45 #endregion 46 47 #region IServiceLocator 48 49 IServiceLocator IIocManager.ServiceLocatorCurrent 50 { 51 get => ServiceLocatorCurrent; 52 set => ServiceLocatorCurrent = value; 53 } 54 55 /// <summary> 56 /// ServiceLocator 57 /// </summary> 58 public static IServiceLocator ServiceLocatorCurrent { get; set; } 59 60 #endregion 61 62 #region IServiceCollection 63 64 IServiceCollection IServiceCollectionResolve.ServiceCollection 65 { 66 get => ServiceCollection; 67 set => ServiceCollection = value; 68 } 69 70 /// <summary> 71 /// ServiceCollection 72 /// </summary> 73 public static IServiceCollection ServiceCollection { get; set; } 74 75 #endregion 76 77 /// <summary> 78 /// IocManager 79 /// </summary> 80 static IocManager() 81 { 82 Instance = new IocManager(); 83 } 84 85 /// <summary> 86 /// SetContainer 87 /// </summary> 88 /// <param name="containerBuilder"></param> 89 public void SetContainer(ContainerBuilder containerBuilder) 90 { 91 ContainerBuilder = containerBuilder; 92 var container = containerBuilder.Build(); 93 IocContainer = container; 94 95 //設(shè)置定位器 96 ServiceLocatorCurrent = new AutofacServiceLocator(IocContainer); 97 } 98 99 /// <summary> 100 /// SetServiceCollection 101 /// </summary> 102 /// <param name="serviceCollection"></param> 103 public void SetServiceCollection(IServiceCollection serviceCollection) 104 { 105 ServiceCollection = serviceCollection; 106 } 107 108 /// <summary> 109 /// UpdateContainer 110 /// </summary> 111 /// <param name="containerBuilder"></param> 112 [Obsolete("Containers should generally be considered immutable. Register all of your dependencies before building/resolving. If you need to change the contents of a container, you technically should rebuild the container. This method may be removed in a future major release.")] 113 public void UpdateContainer(ContainerBuilder containerBuilder) 114 { 115 ContainerBuilder = containerBuilder; 116 containerBuilder?.Update(IocContainer); 117 } 118 119 /// <summary> 120 /// resolve T by lifetime scope 121 /// </summary> 122 /// <typeparam name="T"></typeparam> 123 /// <param name="scope"></param> 124 /// <returns></returns> 125 public T Resolve<T>(ILifetimeScope scope = null) 126 { 127 if (scope == null) 128 { 129 scope = Scope(); 130 } 131 return scope.Resolve<T>(); 132 } 133 134 /// <summary> 135 /// Resolve 136 /// </summary> 137 /// <typeparam name="T"></typeparam> 138 /// <param name="parameters"></param> 139 /// <param name="scope"></param> 140 /// <returns></returns> 141 public T Resolve<T>(IEnumerable<Parameter> parameters, ILifetimeScope scope = null) 142 { 143 if (scope == null) 144 { 145 scope = Scope(); 146 } 147 return scope.Resolve<T>(parameters); 148 } 149 150 /// <summary> 151 /// Resolve 152 /// </summary> 153 /// <typeparam name="T"></typeparam> 154 /// <param name="parameters"></param> 155 /// <returns></returns> 156 public T ResolveParameter<T>(Parameter[] parameters) 157 { 158 var scope = Scope(); 159 return scope.Resolve<T>(parameters); 160 } 161 162 /// <summary> 163 /// ResolveName 164 /// </summary> 165 /// <typeparam name="T"></typeparam> 166 /// <returns></returns> 167 public T ResolveName<T>(string name) 168 { 169 var scope = Scope(); 170 var item = scope.ResolveNamed<T>(name); 171 return item; 172 } 173 174 /// <summary> 175 /// 176 /// </summary> 177 /// <param name="type"></param> 178 /// <returns></returns> 179 public object Resolve(Type type) 180 { 181 var scope = Scope(); 182 var item = scope.Resolve(type); 183 return item; 184 } 185 186 /// <summary> 187 /// IsRegistered 188 /// </summary> 189 /// <typeparam name="T"></typeparam> 190 /// <returns></returns> 191 public bool IsRegistered<T>() where T : class 192 { 193 return IsRegistered(typeof(T)); 194 } 195 196 /// <summary> 197 /// IsRegistered 198 /// </summary> 199 /// <param name="type"></param> 200 /// <param name="scope"></param> 201 /// <returns></returns> 202 public bool IsRegistered(Type type, ILifetimeScope scope = null) 203 { 204 if (scope == null) 205 { 206 scope = Scope(); 207 } 208 209 return scope.IsRegistered(type); 210 } 211 212 /// <summary> 213 /// release object lifetimescope 214 /// </summary> 215 /// <param name="obj"></param> 216 public void Release(object obj) 217 { 218 } 219 220 /// <summary> 221 /// create ILifetimeScope from container 222 /// </summary> 223 /// <returns></returns> 224 private static ILifetimeScope Scope() 225 { 226 return IocContainer.BeginLifetimeScope(); 227 } 228 229 /// <summary> 230 /// ResolveServiceValue 231 /// </summary> 232 /// <typeparam name="T"></typeparam> 233 /// <returns></returns> 234 public T ResolveServiceValue<T>() where T : class, new() 235 { 236 return ServiceCollection.ResolveServiceValue<T>(); 237 } 238 } View Code說明:
主要依賴于:
?
這些全部完成后 我們需要一個(gè)最終的裝載程序??Bootstrap
1 /// <summary> 2 /// 初始化裝載程序 3 /// </summary> 4 public class Bootstrap 5 { 6 /// <summary> 7 /// _isInit 8 /// </summary> 9 private static bool _isInit; 10 11 /// <summary> 12 /// _iocManager 13 /// </summary> 14 public IIocManager IocManager { get; set; } 15 16 /// <summary> 17 /// StartupModule 18 /// </summary> 19 public Type StartupModule { get; set; } 20 21 /// <summary> 22 /// Instance 23 /// </summary> 24 /// <returns></returns> 25 public static Bootstrap Instance<TStartupModule>() where TStartupModule : WorkDataBaseModule 26 { 27 return new Bootstrap(typeof(TStartupModule)); 28 } 29 30 /// <summary> 31 /// instance bootstrap 32 /// </summary> 33 /// <returns></returns> 34 public static Bootstrap Instance() 35 { 36 return new Bootstrap(); 37 } 38 39 /// <summary> 40 /// Bootstrap 41 /// </summary> 42 public Bootstrap() : this(Dependency.IocManager.Instance) 43 { 44 } 45 46 /// <summary> 47 /// Bootstrap 48 /// </summary> 49 public Bootstrap(Type startupModule) : this(startupModule, Dependency.IocManager.Instance) 50 { 51 } 52 53 /// <summary> 54 /// Bootstrap 55 /// </summary> 56 /// <param name="iocManager"></param> 57 public Bootstrap(IIocManager iocManager) 58 { 59 IocManager = iocManager; 60 } 61 62 /// <summary> 63 /// Bootstrap 64 /// </summary> 65 /// <param name="startupModule"></param> 66 /// <param name="iocManager"></param> 67 public Bootstrap(Type startupModule, IIocManager iocManager) 68 { 69 StartupModule = startupModule; 70 IocManager = iocManager; 71 } 72 73 /// <summary> 74 /// 初始化集成框架(配置方式) 75 /// </summary> 76 [STAThread] 77 public void InitiateConfig(IServiceCollection services, List<string> paths) 78 { 79 if (_isInit) return; 80 var builder = new ContainerBuilder(); 81 82 #region RegisterConfig 83 var config = new ConfigurationBuilder(); 84 config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); 85 if (paths != null) 86 { 87 foreach (var item in paths) 88 { 89 config.AddJsonFile(item); 90 } 91 } 92 93 var module = new ConfigurationModule(config.Build()); 94 builder.RegisterModule(module); 95 96 #endregion 97 98 //注入初始module 99 builder.RegisterModule(new WorkDataModule()); 100 101 IocManager.SetServiceCollection(services); 102 103 builder.Populate(services); 104 105 IocManager.SetContainer(builder); 106 _isInit = true; 107 } 108 109 /// <summary> 110 /// InitiateConfig 111 /// </summary> 112 /// <param name="paths"></param> 113 public void InitiateConfig(List<string> paths) 114 { 115 if (_isInit) return; 116 var builder = new ContainerBuilder(); 117 118 #region RegisterConfig 119 var config = new ConfigurationBuilder(); 120 config.SetBasePath(AppDomain.CurrentDomain.BaseDirectory); 121 if (paths != null) 122 { 123 foreach (var item in paths) 124 { 125 config.AddJsonFile(item); 126 } 127 } 128 129 var module = new ConfigurationModule(config.Build()); 130 builder.RegisterModule(module); 131 132 #endregion 133 134 //注入初始module 135 builder.RegisterModule(new WorkDataModule()); 136 137 IocManager.SetContainer(builder); 138 _isInit = true; 139 } 140 141 /// <summary> 142 /// UpdateContainer 143 /// </summary> 144 /// <param name="services"></param> 145 [Obsolete("Containers should generally be considered immutable. Register all of your dependencies before building/resolving. If you need to change the contents of a container, you technically should rebuild the container. This method may be removed in a future major release.")] 146 public void CoreUpdateContainer(IServiceCollection services) 147 { 148 var builder = new ContainerBuilder(); 149 builder.Populate(services); 150 IocManager.UpdateContainer(builder); 151 } 152 153 154 } View Code?
這樣我們整體的架子就算初步完成了??
擴(kuò)展
1.隨著項(xiàng)目逐漸增大 我們會(huì)有很多很多的接口? 去進(jìn)行注入? 我們? 希望通過反射的方式進(jìn)行注入? 所以我們提供一個(gè)?ITypeFinder 方便進(jìn)行操作
注:??ITypeFinder? 來源于?nopcommerce
代碼位置 :
https://github.com/wulaiwei/WorkData.Core/tree/master/WorkData/WorkData/Extensions/TypeFinders
2. 在?.net Framework? autofac 針對(duì)常量的配置 我們可以采用屬性注入的方式完成? ,但是針對(duì).net core版本 不推薦采用 屬性注入? ,推薦使用core 自帶的文件注入方式
1 public Startup(IHostingEnvironment env) 2 { 3 var builder = new ConfigurationBuilder() 4 .SetBasePath(env.ContentRootPath) 5 .AddJsonFile("Config/appsettings.json", optional: true, reloadOnChange: true) 6 .AddJsonFile($"Config/appsettings.{env.EnvironmentName}.json", optional: true) 7 .AddEnvironmentVariables(); 8 this.Configuration = builder.Build(); 9 } View Code有個(gè)參數(shù)選項(xiàng)? 為?reloadOnChange: true? 即修改后會(huì)自動(dòng)重新加載 ,然后注入至?IServiceCollection既可以
我們可以采用IocManager 進(jìn)行?Resolve 但是你就立馬會(huì)遇到很尷尬的問題? 加入我還沒注入完成 我就需要這個(gè)對(duì)象呢? ?
例如? 加入我需要使用JWT
1 services.AddAuthentication(options => 2 { 3 //認(rèn)證middleware配置 4 options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 5 options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 6 }) 7 .AddJwtBearer(o => 8 { 9 //主要是jwt token參數(shù)設(shè)置 10 o.TokenValidationParameters = new TokenValidationParameters 11 { 12 //Token頒發(fā)機(jī)構(gòu) 13 ValidIssuer = workDataBaseJwt.Issuer, 14 //頒發(fā)給誰 15 ValidAudience = workDataBaseJwt.Audience, 16 //這里的key要進(jìn)行加密,需要引用Microsoft.IdentityModel.Tokens 17 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(workDataBaseJwt.SecretKey)), 18 //ValidateIssuerSigningKey=true, 19 ////是否驗(yàn)證Token有效期,使用當(dāng)前時(shí)間與Token的Claims中的NotBefore和Expires對(duì)比 20 ValidateLifetime = true, 21 ////允許的服務(wù)器時(shí)間偏移量 22 ClockSkew = TimeSpan.Zero 23 }; 24 }); View Code然后就需要這個(gè)對(duì)象?workDataBaseJwt? 所以? 我們這邊需要對(duì)?IServiceCollection? 做個(gè)擴(kuò)展
1 public static class WorkDataServiceCollection 2 { 3 public static T ResolveServiceValue<T>(this IServiceCollection services) where T : class, new() 4 { 5 try 6 { 7 var provider = services.BuildServiceProvider(); 8 var entity = provider.GetRequiredService<IOptions<T>>().Value; 9 return entity; 10 } 11 catch (Exception) 12 { 13 return default(T); 14 } 15 } 16 } View Code這樣我就可以在注入之前進(jìn)行Resolve? ? 即:
1 services.Configure<WorkDataBaseJwt>(Configuration.GetSection("WorkDataBaseJwt")); 2 services.Configure<WorkDataDbConfig>(Configuration.GetSection("WorkDataDbContextConfig")); 3 services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); 4 services.AddTransient<IPrincipal>(provider => 5 provider.GetService<IHttpContextAccessor>().HttpContext.User); 6 7 var workDataBaseJwt = services.ResolveServiceValue<WorkDataBaseJwt>(); View Code最后推薦使用? ?json? 進(jìn)行模塊注冊(cè)
1 { 2 "modules": [ 3 { 4 "type": "WorkData.Web.WorkDataWebModule,WorkData.Web" 5 }, 6 { 7 "type": "WorkData.Domain.EntityFramework.DomainEntityFrameworkModule,WorkData.Domain.EntityFramework" 8 }, 9 { 10 "type": "WorkData.EntityFramework.EntityFrameworkModule,WorkData.EntityFramework" 11 }, 12 { 13 "type": "WorkData.Code.WorkDataCodeModule,WorkData.Code" 14 } 15 ] 16 } View Code關(guān)于在.net core 版本下的完整使用? 可以參考? :
https://github.com/wulaiwei/WorkData.Core/tree/master/WorkData/WorkData.Web
?最后針對(duì)Nancy? 下?WorkData的使用?
主要依賴于
using Autofac;
using Nancy;
using Nancy.Bootstrapper;
using Nancy.Bootstrappers.Autofac;
推薦 下Nancy? ? 雖然 有.net? core 但是? 如果你用Nancy? 后會(huì)發(fā)現(xiàn) 兩者會(huì)有很多相似之處
最主要還是相當(dāng) 輕量級(jí)? 可以高度的自定義? ?
?
轉(zhuǎn)載于:https://www.cnblogs.com/wulaiwei/p/9389736.html
總結(jié)
以上是生活随笔為你收集整理的关于 项目中Ioc基础模块 的搭建 (已适用于.net core / .net Framework / Nancy)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: django-内网项目上线测试部署步骤
- 下一篇: 清理服务器网站日志