net core天马行空系列: 一个接口多个实现类,利用mixin技术通过自定义服务名,实现精准属性注入
Posted hezp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了net core天马行空系列: 一个接口多个实现类,利用mixin技术通过自定义服务名,实现精准属性注入相关的知识,希望对你有一定的参考价值。
系列目录
1.net core天马行空系列:原生DI+AOP实现spring boot注解式编程
2.net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作
哈哈哈哈,大家好,我就是高产似母猪的三合。日常开发中,我们常会遇到这样的场景,一个接口,有多个实现类,在某个业务中,我们希望指定某个实现类,如今网络上常见的解决方案,就是注入一个委托或者利用工厂模式,这些方式虽然能实现功能,但使用起来还是不够优雅,如何才称得上优雅呢?自然是在添加服务的时候给服务指定名称,注入的时候根据名称来进行注入,没错,就是类似spring boot里的@Qualifier注解,那么net core能实现这个注解么?要知道net core里的原生DI并不支持在注册的时候给服务添加名称,所以实现起来也是难上加难,但是,利用动态代理,我们就能完美解决这个问题,没错,他Lei了,net core版[QualifierAttribute],实现这个注解用到的最核心的技术,就是动态代理中非常重要的mixin,什么是mixin呢?玩过less的小伙伴可能会知道,这玩意可以把2个css样式混合成一个,而在net core中,DP(castle.dynamicproxy)提供的mixin技术,可以在程序运行时,把多个类混合成一个动态代理类,这个混合类既可以用作类1,又可以用作类2,那么思路就很清晰了,类1就是我们注入的服务类,类2需要我们自定义,在我们的这个自定义类中,我们可以添加一个Name的属性,这样在注册服务的时候给服务命名,到了注入的时候,我们就可以根据名称来进行精准地的属性注入。BTW,动态代理,注解式编程,AOP贯穿本系列始终,no bb,正文开始。
1.定义用到的类
定义QualifierAttribute注解
public class QualifierAttribute : Attribute public string Name get; public QualifierAttribute(string name) if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("服务名称不能为空"); this.Name = name;
定义用来进行混合的附加类接口IDependencyAddition和他的实现类DependencyAddition,类里面只有一个名称的属性。
public interface IDependencyAddition string Name set; get;
public class DependencyAddition : IDependencyAddition public string Name set; get;
第一篇文章里,讲过有一辆汽车,汽车里有引擎,我们在这基础上进行扩展,汽车总是有轮胎的,我们定义一个轮胎接口IWheel和他的2个实现类WheelA和WheelB,类里只有一个Scroll方法,表示轮胎在滚,同时向控制台输出他们是哪种轮胎。
public interface IWheel void Scroll();
public class WheelA : IWheel public void Scroll() Console.WriteLine("我是A轮胎,我正在滚");
public class WheelB : IWheel public void Scroll() Console.WriteLine("我是B轮胎,我正在滚");
修改汽车类接口ICar和他的实现类Car,主要就是添加了轮胎的属性注入,并且用[Qualifier("B轮胎")]注解指定使用哪种轮胎,然后调用Scroll方法,让轮胎滚起来。
public interface ICar Engine Engine set; get; IWheel Wheel set; get; void Fire();
public class Car : ICar [Autowired] public Engine Engine set; get; [Value("oilNo")] public int OilNo set; get; [Autowired] [Qualifier("B轮胎")] public IWheel Wheel set; get; [Transactional] public void Fire() Console.WriteLine("加满" + OilNo + "号汽油,点火"); Wheel.Scroll(); Engine.Start();
IServiceCollection的静态扩展类SummerBootExtentions,和上一篇相比,主要是添加了AddSbService的新重载,使注册服务的时候能够为服务添加名称,在这基础上进一步封装了AddSbSingleton,AddSbScoped和AddSbTransient方法。还有在工厂委托函数里进行属性注入的时候,添加了支持[QualifierAttribute]注解的逻辑,完整类代码如下:
public static class SummerBootExtentions /// <summary> /// 瞬时 /// </summary> /// <typeparam name="TService"></typeparam> /// <typeparam name="TImplementation"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbTransient<TService, TImplementation>(this IServiceCollection services, params Type[] interceptorTypes) return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Transient, interceptorTypes); /// <summary> /// 瞬时 /// </summary> /// <param name="services"></param> /// <param name="serviceType"></param> /// <param name="implementationType"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbTransient(this IServiceCollection services, Type serviceType, Type implementationType, params Type[] interceptorTypes) return services.AddSbService(serviceType, implementationType, ServiceLifetime.Transient, interceptorTypes); /// <summary> /// 请求级别 /// </summary> /// <typeparam name="TService"></typeparam> /// <typeparam name="TImplementation"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbScoped<TService, TImplementation>(this IServiceCollection services, params Type[] interceptorTypes) return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Scoped, interceptorTypes); /// <summary> /// 请求级别 /// </summary> /// <param name="services"></param> /// <param name="serviceType"></param> /// <param name="implementationType"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbScoped(this IServiceCollection services, Type serviceType, Type implementationType, params Type[] interceptorTypes) return services.AddSbService(serviceType, implementationType, ServiceLifetime.Scoped, interceptorTypes); /// <summary> /// 单例 /// </summary> /// <typeparam name="TService"></typeparam> /// <typeparam name="TImplementation"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbSingleton<TService, TImplementation>(this IServiceCollection services, params Type[] interceptorTypes) return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Singleton, interceptorTypes); /// <summary> /// 单例 /// </summary> /// <param name="services"></param> /// <param name="serviceType"></param> /// <param name="implementationType"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbSingleton(this IServiceCollection services, Type serviceType, Type implementationType, params Type[] interceptorTypes) return services.AddSbService(serviceType, implementationType, ServiceLifetime.Singleton, interceptorTypes); public static IServiceCollection AddSbService(this IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime lifetime, params Type[] interceptorTypes) services.Add(new ServiceDescriptor(implementationType, implementationType, lifetime)); object Factory(IServiceProvider provider) var target = provider.GetService(implementationType); var properties = implementationType.GetTypeInfo().DeclaredProperties; foreach (PropertyInfo info in properties) //属性注入 if (info.GetCustomAttribute<AutowiredAttribute>() != null) var propertyType = info.PropertyType; object impl = null; var qualifierAttribute = info.GetCustomAttribute<QualifierAttribute>(); if (qualifierAttribute != null) var serviceName = qualifierAttribute.Name; var implList = provider.GetServices(propertyType); foreach (var tmpImpl in implList) if (ProxyUtil.IsProxy(tmpImpl)) var addition = tmpImpl as IDependencyAddition; if (addition?.Name == serviceName) impl = tmpImpl; break; ; else impl = provider.GetService(propertyType); if (impl != null) info.SetValue(target, impl); //配置值注入 if (info.GetCustomAttribute<ValueAttribute>() is ValueAttribute valueAttribute) var value = valueAttribute.Value; if (provider.GetService(typeof(IConfiguration)) is IConfiguration configService) var pathValue = configService.GetSection(value).Value; if (pathValue != null) var pathV = Convert.ChangeType(pathValue, info.PropertyType); info.SetValue(target, pathV); List<IInterceptor> interceptors = interceptorTypes.ToList() .ConvertAll<IInterceptor>(interceptorType => provider.GetService(interceptorType) as IInterceptor); var proxyGenerator = provider.GetService<ProxyGenerator>(); var proxy = proxyGenerator.CreateInterfaceProxyWithTarget(serviceType, target, interceptors.ToArray()); return proxy; ; var serviceDescriptor = new ServiceDescriptor(serviceType, Factory, lifetime); services.Add(serviceDescriptor); return services; /// <summary> /// 瞬时 /// </summary> /// <typeparam name="TService"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbTransient<TService>(this IServiceCollection services, params Type[] interceptorTypes) return services.AddSbService(typeof(TService), ServiceLifetime.Transient, interceptorTypes); /// <summary> /// 瞬时 /// </summary> /// <param name="services"></param> /// <param name="serviceType"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbTransient(this IServiceCollection services, Type serviceType, params Type[] interceptorTypes) return services.AddSbService(serviceType, ServiceLifetime.Transient, interceptorTypes); /// <summary> /// 请求 /// </summary> /// <typeparam name="TService"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbScoped<TService>(this IServiceCollection services, params Type[] interceptorTypes) return services.AddSbService(typeof(TService), ServiceLifetime.Scoped, interceptorTypes); /// <summary> /// 请求 /// </summary> /// <param name="services"></param> /// <param name="serviceType"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbScoped(this IServiceCollection services, Type serviceType, params Type[] interceptorTypes) return services.AddSbService(serviceType, ServiceLifetime.Scoped, interceptorTypes); /// <summary> /// 单例 /// </summary> /// <typeparam name="TService"></typeparam> /// <param name="services"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbSingleton<TService>(this IServiceCollection services, params Type[] interceptorTypes) return services.AddSbService(typeof(TService), ServiceLifetime.Singleton, interceptorTypes); /// <summary> /// 单例 /// </summary> /// <param name="services"></param> /// <param name="serviceType"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbSingleton(this IServiceCollection services, Type serviceType, params Type[] interceptorTypes) return services.AddSbService(serviceType, ServiceLifetime.Singleton, interceptorTypes); public static IServiceCollection AddSbService(this IServiceCollection services, Type serviceType, ServiceLifetime lifetime, params Type[] interceptorTypes) if (services == null) throw new ArgumentNullException(nameof(services)); if (serviceType == (Type)null) throw new ArgumentNullException(nameof(serviceType)); object Factory(IServiceProvider provider) List<IInterceptor> interceptors = interceptorTypes.ToList() .ConvertAll<IInterceptor>(interceptorType => provider.GetService(interceptorType) as IInterceptor); var proxyGenerator = provider.GetService<ProxyGenerator>(); var proxy = proxyGenerator.CreateClassProxy(serviceType, interceptors.ToArray()); var properties = serviceType.GetTypeInfo().DeclaredProperties; foreach (PropertyInfo info in properties) //属性注入 if (info.GetCustomAttribute<AutowiredAttribute>() != null) var propertyType = info.PropertyType; object impl = null; var qualifierAttribute = info.GetCustomAttribute<QualifierAttribute>(); if (qualifierAttribute != null) var serviceName = qualifierAttribute.Name; var implList = provider.GetServices(propertyType); foreach (var tmpImpl in implList) if (ProxyUtil.IsProxy(tmpImpl)) var addition = tmpImpl as IDependencyAddition; if (addition?.Name == serviceName) impl = tmpImpl; break; ; else impl = provider.GetService(propertyType); if (impl != null) info.SetValue(proxy, impl); //配置值注入 if (info.GetCustomAttribute<ValueAttribute>() is ValueAttribute valueAttribute) var value = valueAttribute.Value; if (provider.GetService(typeof(IConfiguration)) is IConfiguration configService) var pathValue = configService.GetSection(value).Value; if (pathValue != null) var pathV = Convert.ChangeType(pathValue, info.PropertyType); info.SetValue(proxy, pathV); return proxy; ; var serviceDescriptor = new ServiceDescriptor(serviceType, Factory, lifetime); services.Add(serviceDescriptor); return services; /// <summary> /// 添加summer boot扩展 /// </summary> /// <param name="builder"></param> /// <returns></returns> public static IMvcBuilder AddSB(this IMvcBuilder builder) if (builder == null) throw new ArgumentNullException(nameof(builder)); ControllerFeature feature = new ControllerFeature(); builder.PartManager.PopulateFeature<ControllerFeature>(feature); foreach (Type type in feature.Controllers.Select<TypeInfo, Type>((Func<TypeInfo, Type>)(c => c.AsType()))) builder.Services.TryAddTransient(type, type); builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, SbControllerActivator>()); return builder; public static IServiceCollection AddSbRepositoryService(this IServiceCollection services, params Type[] interceptorTypes) var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(it => it.GetTypes()); var tableType = types.Where(it => it.GetCustomAttribute<TableAttribute>() != null); foreach (var type in tableType) var injectServiceType = typeof(IRepository<>).MakeGenericType(type); var injectImplType = typeof(BaseRepository<>).MakeGenericType(type); services.AddSbScoped(injectServiceType, injectImplType, interceptorTypes); return services; /// <summary> /// 瞬时 /// </summary> /// <typeparam name="TService"></typeparam> /// <typeparam name="TImplementation"></typeparam> /// <param name="services"></param> /// <param name="name"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbTransient<TService, TImplementation>(this IServiceCollection services, string name = "", params Type[] interceptorTypes) return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Transient, name, interceptorTypes); /// <summary> /// 瞬时 /// </summary> /// <param name="services"></param> /// <param name="serviceType"></param> /// <param name="implementationType"></param> /// <param name="name"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbTransient(this IServiceCollection services, Type serviceType, Type implementationType, string name = "", params Type[] interceptorTypes) return services.AddSbService(serviceType, implementationType, ServiceLifetime.Transient, name, interceptorTypes); /// <summary> /// 请求级别 /// </summary> /// <typeparam name="TService"></typeparam> /// <typeparam name="TImplementation"></typeparam> /// <param name="services"></param> /// <param name="name"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbScoped<TService, TImplementation>(this IServiceCollection services, string name = "", params Type[] interceptorTypes) return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Scoped, name, interceptorTypes); /// <summary> /// 请求级别 /// </summary> /// <param name="services"></param> /// <param name="serviceType"></param> /// <param name="implementationType"></param> /// <param name="name"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbScoped(this IServiceCollection services, Type serviceType, Type implementationType, string name = "", params Type[] interceptorTypes) return services.AddSbService(serviceType, implementationType, ServiceLifetime.Scoped, name, interceptorTypes); /// <summary> /// 单例 /// </summary> /// <typeparam name="TService"></typeparam> /// <typeparam name="TImplementation"></typeparam> /// <param name="services"></param> /// <param name="name"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbSingleton<TService, TImplementation>(this IServiceCollection services, string name = "", params Type[] interceptorTypes) return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Singleton, name, interceptorTypes); /// <summary> /// 单例 /// </summary> /// <param name="services"></param> /// <param name="serviceType"></param> /// <param name="implementationType"></param> /// <param name="name"></param> /// <param name="interceptorTypes"></param> /// <returns></returns> public static IServiceCollection AddSbSingleton(this IServiceCollection services, Type serviceType, Type implementationType, string name = "", params Type[] interceptorTypes) return services.AddSbService(serviceType, implementationType, ServiceLifetime.Singleton, name, interceptorTypes); public static IServiceCollection AddSbService(this IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime lifetime, string name = "", params Type[] interceptorTypes) services.Add(new ServiceDescriptor(implementationType, implementationType, lifetime)); object Factory(IServiceProvider provider) var target = provider.GetService(implementationType); var properties = implementationType.GetTypeInfo().DeclaredProperties; foreach (PropertyInfo info in properties) //属性注入 if (info.GetCustomAttribute<AutowiredAttribute>() != null) var propertyType = info.PropertyType; object impl = null; var qualifierAttribute = info.GetCustomAttribute<QualifierAttribute>(); if (qualifierAttribute != null) var serviceName = qualifierAttribute.Name; var implList = provider.GetServices(propertyType); foreach (var tmpImpl in implList) if (ProxyUtil.IsProxy(tmpImpl)) var addition = tmpImpl as IDependencyAddition; if (addition?.Name == serviceName) impl = tmpImpl; break; ; else impl = provider.GetService(propertyType); if (impl != null) info.SetValue(target, impl); //配置值注入 if (info.GetCustomAttribute<ValueAttribute>() is ValueAttribute valueAttribute) var value = valueAttribute.Value; if (provider.GetService(typeof(IConfiguration)) is IConfiguration configService) var pathValue = configService.GetSection(value).Value; if (pathValue != null) var pathV = Convert.ChangeType(pathValue, info.PropertyType); info.SetValue(target, pathV); List<IInterceptor> interceptors = interceptorTypes.ToList() .ConvertAll<IInterceptor>(interceptorType => provider.GetService(interceptorType) as IInterceptor); var proxyGenerator = provider.GetService<ProxyGenerator>(); var options = new ProxyGenerationOptions(); options.AddMixinInstance(new DependencyAddition() Name = name ); var proxy = proxyGenerator.CreateInterfaceProxyWithTarget(serviceType, target, options, interceptors.ToArray()); return proxy; ; var serviceDescriptor = new ServiceDescriptor(serviceType, Factory, lifetime); services.Add(serviceDescriptor); return services;
修改第一篇中自定义的控制器激活类SbControllerActivator,添加支持[QualifierAttribute]注解的逻辑。
public class SbControllerActivator : IControllerActivator /// <inheritdoc /> public object Create(ControllerContext actionContext) if (actionContext == null) throw new ArgumentNullException(nameof(actionContext)); Type serviceType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType(); var target = actionContext.HttpContext.RequestServices.GetRequiredService(serviceType); var properties = serviceType.GetTypeInfo().DeclaredProperties; var proxyGenerator = actionContext.HttpContext.RequestServices.GetService<ProxyGenerator>(); var proxy = proxyGenerator.CreateClassProxyWithTarget(serviceType, target); foreach (PropertyInfo info in properties) //属性注入 if (info.GetCustomAttribute<AutowiredAttribute>() != null) var propertyType = info.PropertyType; object impl = null; var qualifierAttribute = info.GetCustomAttribute<QualifierAttribute>(); if (qualifierAttribute != null) var serviceName = qualifierAttribute.Name; var implList = actionContext.HttpContext.RequestServices.GetServices(propertyType); foreach (var tmpImpl in implList) if (ProxyUtil.IsProxy(tmpImpl)) var addition = tmpImpl as IDependencyAddition; if (addition?.Name == serviceName) impl = tmpImpl; break; ; else impl = actionContext.HttpContext.RequestServices.GetService(propertyType); if (impl != null) info.SetValue(proxy, impl); //配置值注入 if (info.GetCustomAttribute<ValueAttribute>() is ValueAttribute valueAttribute) var value = valueAttribute.Value; if (actionContext.HttpContext.RequestServices.GetService(typeof(IConfiguration)) is IConfiguration configService) var pathValue = configService.GetSection(value).Value; if (pathValue != null) var pathV = Convert.ChangeType(pathValue, info.PropertyType); info.SetValue(proxy, pathV); return proxy; /// <inheritdoc /> public virtual void Release(ControllerContext context, object controller)
修改Startup.cs类里的ConfigureServices方法,可以看到,我们在注册WheelA和WheelB的时候,为服务添加了名称,并且随手添加了个拦截器,代码如下:
public void ConfigureServices(IServiceCollection services) services.Configure<CookiePolicyOptions>(options => // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; ); services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_1) .AddSB(); services.AddSingleton<ProxyGenerator>(); services.AddSbScoped<Engine>(typeof(TransactionalInterceptor)); services.AddSbScoped<IUnitOfWork, UnitOfWork>(); services.AddScoped(typeof(TransactionalInterceptor)); services.AddSbScoped<ICar, Car>(typeof(TransactionalInterceptor)); services.AddSbScoped<IDbFactory, DbFactory>(); services.AddSbRepositoryService(typeof(TransactionalInterceptor)); services.AddSbScoped<IAddOilService, AddOilService>(typeof(TransactionalInterceptor)); services.AddSbScoped<IWheel, WheelA>("A轮胎", typeof(TransactionalInterceptor)); services.AddSbScoped<IWheel, WheelB>("B轮胎", typeof(TransactionalInterceptor));
控制器类HomeController,代码如下:
public class HomeController : Controller [Autowired] public ICar Car set; get; [Autowired] public IDistributedCache Cache set; get; [Value("description")] public string Description set; get; [Autowired] public IAddOilService AddOilService set; get; public IActionResult Index() var car = Car; AddOilService.AddOil(); Car.Fire(); Console.WriteLine(Description); return View();
核心原理,从SummerBootExtentions类完整源码里单独抽取mixin服务注册和属性注入的代码,可以看到在生成动态代理的时候,在option里添加了mixin实例,也就是我们自定义的DependencyAddition,里面包含了这个服务的名称,在注入时,判断是否有[QualifierAttribute]注解,如果有,就获取整个接口的实现类列表,在循环里,判断是否为动态代理类,如果是,就把每个类实例强转为附加类接口IDependencyAddition,然后获取名称,如果名称和我们[QualifierAttribute]注解指定的名称相同,则注入。
var proxyGenerator = provider.GetService<ProxyGenerator>(); var options = new ProxyGenerationOptions(); options.AddMixinInstance(new DependencyAddition() Name = name ); var proxy = proxyGenerator.CreateInterfaceProxyWithTarget(serviceType, target, options, interceptors.ToArray());
//属性注入 if (info.GetCustomAttribute<AutowiredAttribute>() != null) var propertyType = info.PropertyType; object impl = null; var qualifierAttribute = info.GetCustomAttribute<QualifierAttribute>(); if (qualifierAttribute != null) var serviceName = qualifierAttribute.Name; var implList = provider.GetServices(propertyType); foreach (var tmpImpl in implList) if (ProxyUtil.IsProxy(tmpImpl)) var addition = tmpImpl as IDependencyAddition; if (addition?.Name == serviceName) impl = tmpImpl; break; ; else impl = provider.GetService(propertyType); if (impl != null) info.SetValue(target, impl);
2.效果图
先注入A轮胎
从上图可以看到,生成的动态代理类里包含了Name这个属性,并且名称为A轮胎。
控制台输出,也显示了“我是A轮胎,我正在滚”,符合预期。
注入B轮胎
从上图可以看到,生成的动态代理类里包含了Name这个属性,并且名称为B轮胎。
控制台输出,也显示了“我是B轮胎,我正在滚”,符合预期。
3. 写在最后
通过[Autowired]注解和[Qualifier("B轮胎")]注解联合使用,我们就实现了一个接口多个实现类的精准属性注入,代码看起来是不是舒服多了呢~,我始终相信,如果一件事我讲的别人听不明白,那一定是我讲的不够有趣,不够通俗易懂,所以大白话是必须的,如果文章还有哪里说得太深奥,欢迎给我指出来,同时net core里还有啥功能让你觉得用起来特别别扭的,请!一定!告诉我!,i will try it,因为这特别有趣,因为我一直践行的编程理念就是万物靠注入,万物可拦截,万物皆注解,始终相信IOC和AOP是构建软件的基石,哈哈哈哈哈。
如果这篇文章对你有所帮助,不妨点个赞咯。
以上是关于net core天马行空系列: 一个接口多个实现类,利用mixin技术通过自定义服务名,实现精准属性注入的主要内容,如果未能解决你的问题,请参考以下文章
net core天马行空系列:降低net core门槛,数据库操作和http访问仅需写接口,实现类由框架动态生成
Asp.NET Core一个接口的多个实现如何基于当前HTTP请求注册
Asp.NET Core 一个接口的多个实现如何通过 DI 注册?