Netcore中实现字段和属性注入
https://www.cnblogs.com/loogn/p/10566510.html
简单来说,使用Ioc模式需要两个步骤,第一是把服务注册到容器中,第二是从容器中获取服务,我们一个一个讨论并演化。这里不会考虑使用如Autofac等第三方的容器来代替默认容器,只是提供一些简单实用的小方法用于简化应用层的开发。
将服务注入到容器
asp.netcore官方给出的在容器中注册服务方法是,要在Startup类的ConfigureServices方法中添加服务,如下所示:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton(typeof(UserService));
services.AddSingleton(typeof(MsgService));
services.AddSingleton(typeof(OrderService));
}
AddMvc方法添加了mvc模块内部用到的一些服务,这个是封装好的,一句话就行了,其他第三方组件也都提供了类似的Add方法,把自己内部需要的服务都封装好注册进去了。但是我们应用开发人员使用的类,还是需要一个一个写进去的,大家最常见的三层架构中的数据访问层和业务逻辑层便是此类服务,上面代码中我加入了三个业务服务类。这显然不是长久之计,我想大家在开发中也会针对此问题做一些处理,这里说下我的,仅供参考吧。
解决方法就是批量注册!说到批量,就需要一个东西来标识一批东西,然后用这一个东西来控制这一批东西。在.net程序的世界中,有两个可选的角色,一个是接口Interface,另一个是特性Attribute。
如果使用接口作为标识来使用,限制就太死板了,一个标识的信息不是绝对的单一,是不推荐使用接口的,因为可能需要引入多个接口才能共同完成,所以我选择特性作为标识。特性相较与接口有什么特点呢?特性在运行时是类的实例,所以可以存储更多的信息。
下面我们简单实现一个AppServiceAttribute:
/// <summary>
/// 标记服务
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class AppServiceAttribute : Attribute
{
}
这个特性类取名AppService有两个理由,一是指定是应用层的服务类,二是避免使用Service这样的通用命名和其他类库冲突。
有了标识,就可以批量处理了,我们在一个新的类中给IServiceCollection提供一个扩展方法,用来批量添加标记有AppService特性的服务到容器中。
public static class AppServiceExtensions
{
/// <summary>
/// 注册应用程序域中所有有AppService特性的服务
/// </summary>
/// <param name="services"></param>
public static void AddAppServices(this IServiceCollection services)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in assembly.GetTypes())
{
var serviceAttribute = type.GetCustomAttribute<AppServiceAttribute>();
if (serviceAttribute != null)
{
services.AddSingleton(type);
}
}
}
}
}
我们遍历应用程序中所有程序集,然后嵌套遍历每个程序集中的所有类型,判断类型是否有AppService特性,如果有的话就添加到容器中,这里有点不自信哦,为什么呢,因为我是使用AddSingleton方法以单例模式将服务添加到容器中的,虽然三层中的数据访问层和业务逻辑层绝大部分都可以使用单例,但是我们希望更通用一些,大家都知道netcore自带的Ioc容器支持三种生命周期,所以我们修改AppServiceAttribute,添加一个Lifetime属性:
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class AppServiceAttribute : Attribute
{
/// <summary>
/// 生命周期
/// </summary>
public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Singleton;
}
Lifetime的默认值我们设置成ServiceLifetime.Singleton是比较合适的,因为大部分服务我们都希望使用单例注册,一个合理的默认设置可以节省使用者很多代码,新手可能还会乐于复制粘贴,但老同志肯定都深有体会。
有了Lifetime这个信息,我们就可以改进AddAppServices方法了,在判断serviceAttribute不为null后,使用下面的代码替换services.AddSingleton(type):
switch (serviceAttribute.Lifetime)
{
case ServiceLifetime.Singleton:
services.AddSingleton(serviceType, type);
break;
case ServiceLifetime.Scoped:
services.AddScoped(serviceType, type);
break;
case ServiceLifetime.Transient:
services.AddTransient(serviceType, type);
break;
default:
break;
}
现在我们可以注册不同生命周期的服务了,只是该控制是在类的定义中,按理说,服务对象注册到容器中的生命周期,是不应该在类的定义中确定的,因为一个类的定义是独立的,定义好之后,使用者可以用任何一种容器支持的生命周期来注册实例。但是此时这样的设计是比较合理的,因为我们要解决的是应用层服务的批量注册,这类服务一般在定义的时候就已经确定了使用方式,而且很多时候服务的开发者就是该服务的使用者!所以我们可以把这个当成合理的反范式设计。
目前这样子,对于我来说,基本已经够用了,因为在应用层,我都是依赖实现编程的
Netcore中实现字段和属性注入的更多相关文章
- 在netcore中实现字段和属性注入
简单来说,使用Ioc模式需要两个步骤,第一是把服务注册到容器中,第二是从容器中获取服务,我们一个一个讨论并演化.这里不会考虑使用如Autofac等第三方的容器来代替默认容器,只是提供一些简单实用的小方 ...
- C#中的字段与属性的区别及属性的作用
C#中的字段与属性的区别及属性的作用 先上代码 public class Employee { //字段 private string name; //属性 public string Name { ...
- C#类中的字段、属性和方法
C#类中的字段.属性和方法 刚开始学C#,对于类中的字段.属性和方法很难分清,写下这份笔记,帮助理解 字段:与类相关的变量 声明方法与声明变量类似,可在前面添加访问修饰符.static关键字等: 属性 ...
- ASP.NET Core中使用Autofac进行属性注入
一些无关紧要的废话: 作为一名双修程序员(自封的),喜欢那种使用Spring的注解形式进行依赖注入或者Unity的特性形式进行依赖注入,当然,形式大同小异,但结果都是一样的,通过属性进行依赖注入. A ...
- C#反射类中所有字段,属性,方法
可能大家都知道在C#中如何创建一个类,但对于类的结构可能大家不一定了解的很清楚,对于我来说,我之前也搞的不是很明白,今天,当我没事研究反射的时候突然发现了着一点.我们来看类的结构到底是什么 publi ...
- Java中的字段和属性
Java中的属性,通常可以理解为get和set方法.而字段,通常叫做“类成员”. 属性只局限于类中方法的声明,并不与类中其他成员相关.例如:void setA(String s){}String ge ...
- django中model字段与属性
model field 类型1.AutoField 一个自增的IntegerField,一般不直接使用,Django会自动给每张表添加一个自增的primary key. 2.BigIntege ...
- SQLSERVER获取数据库中的所有表的名称、表中所有字段的属性
1.查询数据库中的所有数据库名: SELECT Name FROM Master..SysDatabases ORDER BY Name 2.查询某个数据库中所有的表名: SELECT Name FR ...
- ms sql 根据表名查询 表中所有字段的属性值 sql语句
SELECT表名=case when a.colorder=1 then d.name else '' end,--表说明=case when a.colorder=1 then isnull(f.v ...
随机推荐
- 2018网络预选赛 青岛 H
题目链接:https://pintia.cn/problem-sets/1036903825309761536/problems/1041156323504345088 题意:小明从某一点出发,向右方 ...
- 100722E The Bookcase
传送门 题目大意 给你一些书的高度和宽度,有一个一列三行书柜,要求放进去书后,三行书柜的高的和乘以书柜的宽度最小.问这个值最小是多少. 分析 我们可以先将所有书按照高度降序排好,这样对于每一层只要放过 ...
- git命令(二)
2.查看.添加.提交.删除.找回,重置修改文件 3.查看文件diff git diff <file> # 比较当前文件和暂存区文件差异 git diff git diff <$id1 ...
- [转载]应用 Valgrind 发现 Linux 程序的内存问题
应用 Valgrind 发现 Linux 程序的内存问题 如何定位应用程序开发中的内存问题,一直是 inux 应用程序开发中的瓶颈所在.有一款非常优秀的 linux 下开源的内存问题检测工具:valg ...
- css css3新特性
css css3新特性 一.css3是什么? 我不喜欢把已有的概念从一个地方抄到另一个地方,还是喜欢如下方式. 参考百度百科: http://baike.baidu.com/link?url=z2V ...
- log log4j配置
log4j配置 日志路径: web方式:web.xml <context-param> <param-name>log4jConfigLocation</param- ...
- Python3下的paramiko模块
paramiko模块是基于Python实现的SSH远程安全连接,用于SSH远程执行命令.文件传输等功能. 默认Python没有,需要手动安装:pip install paramiko SSH密码认证远 ...
- 「ZOJ 1354」Extended Lights Out「高斯消元」
题意:给定一个\(5\times 6\)的棋盘的\(01\)状态,每次操作可以使它自己和周围四个格子状态取反,求如何操作,输出一个\(01\)矩阵 题解:这题可以通过枚举第一行的状态然后剩下递推来做, ...
- Python中__call__的用法
概念: Python中有一个有趣的语法,只要定义类型的时候,实现__call__函数,这个类型就成为可调用的.换句话说,我们可以把这个类型的对象当作函数来使用,相当于 重载了括号运算符. 示例: 所 ...
- GET/POST/g和钩子函数(hook)
GET请求和POST请求: 1. get请求: * 使用场景:如果只对服务器获取数据,并没有对服务器产生任何影响,那么这时候使用get请求. * 传参:get请求传参是放在url中,并且是通过`?`的 ...