在.net core中实现字段和属性注入的示例代码
简单来说,使用Ioc模式需要两个步骤,第一是把服务注册到容器中,第二是从容器中获取服务,我们一个一个讨论并演化。这里不会考虑使用如Autofac等第三方的容器来代替默认容器,只是提供一些简单实用的小方法用于简化应用层的开发。
将服务注入到容器
asp.netcore官方给出的在容器中注册服务方法是,要在Startup类的ConfigureServices方法中添加服务,如下所示:
publicvoidConfigureServices(IServiceCollectionservices) { services.AddMvc(); services.AddSingleton(typeof(UserService)); services.AddSingleton(typeof(MsgService)); services.AddSingleton(typeof(OrderService)); }
AddMvc方法添加了mvc模块内部用到的一些服务,这个是封装好的,一句话就行了,其他第三方组件也都提供了类似的Add方法,把自己内部需要的服务都封装好注册进去了。但是我们应用开发人员使用的类,还是需要一个一个写进去的,大家最常见的三层架构中的数据访问层和业务逻辑层便是此类服务,上面代码中我加入了三个业务服务类。这显然不是长久之计,我想大家在开发中也会针对此问题做一些处理,这里说下我的,仅供参考吧。
解决方法就是批量注册!说到批量,就需要一个东西来标识一批东西,然后用这一个东西来控制这一批东西。在.net程序的世界中,有两个可选的角色,一个是接口Interface,另一个是特性Attribute。
如果使用接口作为标识来使用,限制就太死板了,一个标识的信息不是绝对的单一,是不推荐使用接口的,因为可能需要引入多个接口才能共同完成,所以我选择特性作为标识。特性相较与接口有什么特点呢?特性在运行时是类的实例,所以可以存储更多的信息。
下面我们简单实现一个AppServiceAttribute:
//////标记服务 /// [AttributeUsage(AttributeTargets.Class,Inherited=false)] publicclassAppServiceAttribute:Attribute { }
这个特性类取名AppService有两个理由,一是指定是应用层的服务类,二是避免使用Service这样的通用命名和其他类库冲突。
有了标识,就可以批量处理了,我们在一个新的类中给IServiceCollection提供一个扩展方法,用来批量添加标记有AppService特性的服务到容器中。
publicstaticclassAppServiceExtensions { //////注册应用程序域中所有有AppService特性的服务 /// ///publicstaticvoidAddAppServices(thisIServiceCollectionservices) { foreach(varassemblyinAppDomain.CurrentDomain.GetAssemblies()) { foreach(vartypeinassembly.GetTypes()) { varserviceAttribute=type.GetCustomAttribute (); if(serviceAttribute!=null) { services.AddSingleton(type); } } } } }
我们遍历应用程序中所有程序集,然后嵌套遍历每个程序集中的所有类型,判断类型是否有AppService特性,如果有的话就添加到容器中,这里有点不自信哦,为什么呢,因为我是使用AddSingleton方法以单例模式将服务添加到容器中的,虽然三层中的数据访问层和业务逻辑层绝大部分都可以使用单例,但是我们希望更通用一些,大家都知道netcore自带的Ioc容器支持三种生命周期,所以我们修改AppServiceAttribute,添加一个Lifetime属性:
[AttributeUsage(AttributeTargets.Class,Inherited=false)] publicclassAppServiceAttribute:Attribute { //////生命周期 /// publicServiceLifetimeLifetime{get;set;}=ServiceLifetime.Singleton; }
Lifetime的默认值我们设置成ServiceLifetime.Singleton是比较合适的,因为大部分服务我们都希望使用单例注册,一个合理的默认设置可以节省使用者很多代码,新手可能还会乐于复制粘贴,但老同志肯定都深有体会。
有了Lifetime这个信息,我们就可以改进AddAppServices方法了,在判断serviceAttribute不为null后,使用下面的代码替换services.AddSingleton(type):
switch(serviceAttribute.Lifetime) { caseServiceLifetime.Singleton: services.AddSingleton(serviceType,type); break; caseServiceLifetime.Scoped: services.AddScoped(serviceType,type); break; caseServiceLifetime.Transient: services.AddTransient(serviceType,type); break; default: break; }
现在我们可以注册不同生命周期的服务了,只是该控制是在类的定义中,按理说,服务对象注册到容器中的生命周期,是不应该在类的定义中确定的,因为一个类的定义是独立的,定义好之后,使用者可以用任何一种容器支持的生命周期来注册实例。但是此时这样的设计是比较合理的,因为我们要解决的是应用层服务的批量注册,这类服务一般在定义的时候就已经确定了使用方式,而且很多时候服务的开发者就是该服务的使用者!所以我们可以把这个当成合理的反范式设计。
目前这样子,对于我来说,基本已经够用了,因为在应用层,我都是依赖实现编程的