.netCore+Vue 搭建的简捷开发框架 (5)
文章目录:.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