文章目錄:.netCore+Vue 搭建的簡捷開發框架--目錄
上兩節的內容介紹了一些關於。netCore 相關的一些基礎知識。介紹這些的目的,最主要的還是為了我們的架構搭建服務。
上一節中,我們介紹了有關NetCore DI的一些概念。 整個框架,我們的倉儲層、服務層都是通過依賴注入的方式進行加載調用的。
下面就來看一下倉儲層和服務層是如何注入的:

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using Microsoft.AspNetCore.Builder; 6 using Microsoft.AspNetCore.Hosting; 7 using Microsoft.AspNetCore.Mvc; 8 using Microsoft.EntityFrameworkCore; 9 using Microsoft.Extensions.Configuration; 10 using Microsoft.Extensions.DependencyInjection; 11 using Microsoft.Extensions.Logging; 12 using Microsoft.Extensions.Options; 13 using Sincere.Core.IRepository.Base; 14 using Sincere.Core.IServices.Base; 15 using Sincere.Core.Model.EFCore; 16 using Sincere.Core.Model.Models; 17 using Sincere.Core.Repository.Base; 18 using Sincere.Core.Services.Base; 19 using Sincere.Core.WebAPI.ServiceExtension; 20 21 namespace Sincere.Core.WebAPI 22 { 23 public class Startup 24 { 25 public Startup(IConfiguration configuration) 26 { 27 Configuration = configuration; 28 } 29 30 public IConfiguration Configuration { get; } 31 32 // This method gets called by the runtime. Use this method to add services to the container. 33 public void ConfigureServices(IServiceCollection services) 34 { 35 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); 36 37 services.AddDbContextPool<BaseCoreContext>(options => 38 options.UseSqlServer(BaseDBConfig.ConnectionString)); 39 services.AddScoped<IBaseContext, BaseCoreContext>(); 40 //泛型引用方式 41 services.AddScoped(typeof(IBaseServices<>), typeof(BaseServices<>)); 42 43 services.AddScoped(typeof(IBaseRepository<>), typeof(BaseRepository<>)); 44 45 services.RegisterAssembly("IServices"); 46 services.RegisterAssembly("IRepository"); 47 } 48 49 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 50 public void Configure(IApplicationBuilder app, IHostingEnvironment env) 51 { 52 if (env.IsDevelopment()) 53 { 54 app.UseDeveloperExceptionPage(); 55 } 56 57 app.UseMvc(); 58 } 59 } 60 }
標記1 的部分,是關於EF,以及DbContext 的相關引用,這里涉及到一個AddDbContextPool的概念,我個人的理解就是可以把他當成我們使用ADO.net 時的數據庫連接池。
另外需要說明的一點就是,采用AddScoped 的方式,整個數據庫鏈接的上下文,在整個請求過程中,只會被實例化一次。
標記2的部分,時我們對倉儲的基礎類,和服務層基礎類的引用,注意這些基礎類是泛型的方式,所以引用的時候,需要采用泛型的方式來實現。在這個地方,剛開始的時候,不知道AddScoped支持泛型的方式,還寫過一個工廠類來進行注入。
剩下的方法,services.RegisterAssembly 其實是一個比較巧妙的方式,為了避免每寫一個service、repository就在ConfigureServices中注入一次。所以在這里采用了反射的機制。利用反射和我們的約定,將整個程序集中的Service和Repository進行一個引用。
webAPI工程下新建一個ServiceExtension目錄,並添加RuntimeHelper類和ServiceExtension。
如下圖:
代碼如下:

1 using Microsoft.Extensions.DependencyInjection; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Reflection; 6 using System.Threading.Tasks; 7 8 namespace Sincere.Core.WebAPI.ServiceExtension 9 { 10 /// <summary> 11 /// IServiceCollection擴展 12 /// </summary> 13 /// <summary> 14 /// IServiceCollection擴展 15 /// </summary> 16 public static class ServiceExtension 17 { 18 /// <summary> 19 /// 用DI批量注入接口程序集中對應的實現類。 20 /// <para> 21 /// 需要注意的是,這里有如下約定: 22 /// IUserService --> UserService, IUserRepository --> UserRepository. 23 /// </para> 24 /// </summary> 25 /// <param name="service"></param> 26 /// <param name="interfaceAssemblyName">接口程序集的名稱(不包含文件擴展名)</param> 27 /// <returns></returns> 28 public static IServiceCollection RegisterAssembly(this IServiceCollection service, string interfaceAssemblyName, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped) 29 { 30 if (service == null) 31 throw new ArgumentNullException(nameof(service)); 32 if (string.IsNullOrEmpty(interfaceAssemblyName)) 33 throw new ArgumentNullException(nameof(interfaceAssemblyName)); 34 35 var assembly = RuntimeHelper.GetAssembly(interfaceAssemblyName); 36 if (assembly == null) 37 { 38 throw new DllNotFoundException($"the dll \"{interfaceAssemblyName}\" not be found"); 39 } 40 41 //過濾掉非接口及泛型接口 42 var types = assembly.GetTypes().Where(t => t.GetTypeInfo().IsInterface && !t.GetTypeInfo().IsGenericType); 43 44 foreach (var type in types) 45 { 46 var implementTypeName = type.Name.Substring(1); 47 var implementType = RuntimeHelper.GetImplementType(implementTypeName, type); 48 if (implementType != null) 49 { 50 switch (serviceLifetime) 51 { 52 //根據條件,選擇注冊依賴的方法 53 case ServiceLifetime.Scoped: 54 //將獲取到的接口和類注冊進去 55 service.AddScoped(type, implementType); 56 break; 57 case ServiceLifetime.Singleton: 58 service.AddSingleton(type, implementType); 59 break; 60 case ServiceLifetime.Transient: 61 service.AddTransient(type, implementType); 62 break; 63 } 64 65 } 66 } 67 return service; 68 } 69 70 71 } 72 }

1 using Microsoft.Extensions.DependencyModel; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Reflection; 6 using System.Runtime.Loader; 7 using System.Threading.Tasks; 8 9 namespace Sincere.Core.WebAPI.ServiceExtension 10 { 11 public class RuntimeHelper 12 { 13 /// <summary> 14 /// 獲取項目程序集,排除所有的系統程序集(Microsoft.***、System.***等)、Nuget下載包 15 /// </summary> 16 /// <returns></returns> 17 public static IList<Assembly> GetAllAssemblies() 18 { 19 var list = new List<Assembly>(); 20 var deps = DependencyContext.Default; 21 var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系統程序集、Nuget下載包 22 foreach (var lib in libs) 23 { 24 try 25 { 26 var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); 27 list.Add(assembly); 28 } 29 catch (Exception) 30 { 31 // ignored 32 } 33 } 34 return list; 35 } 36 37 public static Assembly GetAssembly(string assemblyName) 38 { 39 return GetAllAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName)); 40 } 41 42 public static IList<Type> GetAllTypes() 43 { 44 var list = new List<Type>(); 45 foreach (var assembly in GetAllAssemblies()) 46 { 47 var typeInfos = assembly.DefinedTypes; 48 foreach (var typeInfo in typeInfos) 49 { 50 list.Add(typeInfo.AsType()); 51 } 52 } 53 return list; 54 } 55 56 public static IList<Type> GetTypesByAssembly(string assemblyName) 57 { 58 var list = new List<Type>(); 59 var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName)); 60 var typeInfos = assembly.DefinedTypes; 61 foreach (var typeInfo in typeInfos) 62 { 63 list.Add(typeInfo.AsType()); 64 } 65 return list; 66 } 67 68 public static Type GetImplementType(string typeName, Type baseInterfaceType) 69 { 70 return GetAllTypes().FirstOrDefault(t => 71 { 72 if (t.Name == typeName && 73 t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name)) 74 { 75 var typeInfo = t.GetTypeInfo(); 76 return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsGenericType; 77 } 78 return false; 79 }); 80 } 81 } 82 }
這樣類似的代碼網上挺多的。大家可以參考借鑒一下。另外這個地方其實不用太過於關心反射帶來的性能問題,因為只有在程序啟動的時候,加載一次。
倉儲層和服務層都注入進來以后,接下來就是怎么去使用了。
來一起看一下我們是怎么在ValuesController里面進行實現的。
1.定義服務層接口
2.在ValuesController初始化的時候,將服務層接口注入進來。
3.使用接口,調用數據。
代碼如下:

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using Microsoft.AspNetCore.Mvc; 6 using Sincere.Core.IServices; 7 8 namespace Sincere.Core.WebAPI.Controllers 9 { 10 [Route("api/[controller]")] 11 [ApiController] 12 public class ValuesController : ControllerBase 13 { 14 readonly IAdvertisementServices _advertisementServices; 15 public ValuesController(IAdvertisementServices advertisementServices) 16 { 17 _advertisementServices = advertisementServices; 18 } 19 20 // GET api/values 21 [Route("")] 22 [HttpGet] 23 public async Task<ActionResult<IEnumerable<string>>> Get() 24 { 25 var t = await _advertisementServices.ReadAllAd(); 26 27 return new string[] { "value1", "value2" }; 28 } 29 30 // GET api/values/5 31 [HttpGet("{id}")] 32 public ActionResult<string> Get(int id) 33 { 34 return "value"; 35 } 36 37 // POST api/values 38 [HttpPost] 39 public void Post([FromBody] string value) 40 { 41 } 42 43 // PUT api/values/5 44 [HttpPut("{id}")] 45 public void Put(int id, [FromBody] string value) 46 { 47 } 48 49 // DELETE api/values/5 50 [HttpDelete("{id}")] 51 public void Delete(int id) 52 { 53 } 54 } 55 }
關於ReadAllAd()方法,之前的章節中已經有過描述。
調用成功,說明框架的整體流程已經跑通了!晚上加個雞腿,慶祝一下!
按照之前的腦圖,不知道大家還記得不?
紅框里面的內容,到這一節,基本就結束了。接下來,會逐步的豐富我們的框架。加入日志,異常處理,權限驗證。以及其他腦圖中列出來的內容。
希望整個過程對於想學習、了解netCore 的同學們有所幫助!
源碼已經更新:https://github.com/xzhencheng/Sincere.Core