依赖注入在 dotnet core 中实现与使用:2 使用 Extensions Depen
既然是依赖注入容器,必然会涉及到服务的注册,获取服务实例,管理作用域,服务注入这四个方面。
?看起来很复杂,使用的时候其实很简单。 1. 服务注册1.1 支持不同的作用域 Scope
DependencyInjection 通过 Add 方法来进行服务注册,三种不同的作用域通过三种带有不同后缀的 Add 方法来支持。
?
services.AddSingleton<IUserService,UserService>(); services.AddScoped<IUserService,UserService>(); services.AddTransient<IUserService,UserService>(); Singleton 就是单例,以后通过该容器获取出来的,都是同一个服务对象实例。 Scoped 就是限定了作用域,在每个特定的作用域中,只会有一个服务对象实例。作用域需要你自己来创建,后面在使用服务的时候,我们再介绍。 而 Transient 则在每次从容器中获取的时候,都对创建新的服务对象实例。 三种作用域简单明了,后面我们介绍服务注册的时候,就不再关注服务的作用域,都使用单例来介绍,其它两种方式是相同的。 1.2 基于接口注册这是最为常见的注册方式,在实际开发中,服务一般都有对应的接口。为了方便注册,在 .NET Core 中一般使用泛型方式进行注册,这样比较简洁。是最推荐的方式。 services.AddSingleton<IUserService,UserService>(); 当然,也可以使用基于类型的方式注册,不过代码没有使用泛型方式优雅。你需要先获取服务的类型,再通过类型进行注册。 services.AddSingleton(typeof(IUserService),typeof(UserService)); 1.3 直接注册实例如果服务并没有对应的接口,可以直接使用对象实例注册,ServiceCollection 会直接使用该实例类型作为服务接口类型来注册,这种方式比较简单粗暴。 services.AddSingleton(typeof(UserService)); ? 1.4 注册泛型服务泛型是很强大的特性,例如,泛型的仓储,我们只需要一个泛型仓储,就可以通过它访问针对不同类型的数据。 容器必须要支持泛型的服务,例如,针对下面的简化仓储示例,注意这里的 Get() 简化为只返回默认值。 public interface IRepository<T> { T Get(int id); } public class Repository<T>: IRepository<T> { public Repository() { Console.WriteLine(typeof(T).Name); } public T Get(int id){ return default(T); } } 只需要注册一次,就可以获得不同实现的支持。但是,泛型比较特殊,不能这样写: services.AddSingleton<IRepository<>,Repository<>>(); 你需要通过类型的方式来进行服务注册。 services.AddSingleton(typeof(IRepository<>),typeof(Repository<>)); 如果没有这个仓储接口的话,就可以这样注册。 services.AddSingleton(typeof(Repository<>)); 如果涉及到多个类型的泛型,例如仓储的定义变成下面这样,id 的类型使用 K 来指定。 public interface IRepository<T,K> { T Get(K id); } 而仓储的实现也变成下面这样: public class Repository<T,K>: IRepository<T,K> { public Repository() { Console.WriteLine(typeof(T).Name); } public T Get(K id){ return default(T); } } 注册的时候,就需要写成下面的样子: services.AddSingleton (typeof (IRepository<,>),typeof (Repository<,>)); ? 1.5 工厂模式注册除了让容器帮助构造服务对象实例,我们也可以自己定义构建服务对象实例的工厂供容器使用。 如果服务实现没有参数,例如 UserRepository,可以这样写: services.AddSingleton<IUserRepository>( (serviceProvider) => { return new UserReposotory(); } ); 如果构造函数是有参数的,比如: public class UserReposotory: IUserRepository { public UserReposotory(string name){ Console.WriteLine( name); } } 注册就变成了下面的样子,需要注意的是,工厂会得到一个 ServiceProvider 的实例供你使用。 services.AddSingleton<IUserRepository> ((serviceProvider) => { Console.WriteLine ("construction IUserReposority"); return new UserReposotory ("Tom"); }); 1.6 直接使用?
public class ServiceDescriptor { public ServiceLifetime Lifetime { get; } public Type ServiceType { get; } public Type ImplementationType { get; } public object ImplementationInstance { get; } public Func<IServiceProvider,object> ImplementationFactory { get; } } 所以,实际上,你可以通过自己创建这个服务描述对象来进行注册。 services.Add(ServiceDescriptor.Singleton<IUserService,UserService>()); 殊途同归,其实与使用上面的方式效果是一样的。 2 管理 Scope如果注册服务的时候,指定了服务是单例的,无论从哪个 Scope 中获得的服务实例,都会是同一个。所以,单例的服务与 Scope 就没有什么关系了。 如果注册服务的时候,指定了服务是瞬态的,无论从哪个 Scope 中获取服务实例,都会是新创建的,每次获取就新创建一个。所以,瞬态的服务与 Scope 也没有什么关系了。 只有在注册时声明是 Scoped 的服务,才会涉及到 Scope。所以,只有 Scoped 的服务才会涉及到 Scope。 在容器初始化之后,会创建一个根的 Scope 作用域,它是默认的。? IServiceProvider provider = services.BuildServiceProvider (); 在需要作用域的时候,可以通过已经创建的 provider 来创建作用域,然后从这个新创建的作用域再获取当前的服务提供器。 IServiceProvider provider = services.BuildServiceProvider (); IServiceScope scope = provider.CreateScope(); IServiceProvider scopedServiceProvider = scope.ServiceProvider; 通过作用域的服务提供器获取服务对象实例的时候,就会影响到通过 Scoped 注册的服务了。 这种类型的服务在每一个 Scope 中只会创建一个。 在微软的实现中,不支持嵌套的 Scope。 3 注入服务?现在又回到了涉及编程中的使用,在需要服务对象实例的时候,我们只需要注入。 微软的实现实际上只支持了构造函数注入。例如: public class HomeController : Controller { private readonly IDateTime _dateTime; public HomeController(IDateTime dateTime) { _dateTime = dateTime; }
4 常见问题?多次注册问题你可以对同一个服务类型多次注册,这并不会遇到异常。在获取服务对象实例的时候,是通过最后一次注册来支持的。 需要的话,也可以获取到所有注册的服务对象实例。 var instances = provider.GetServices(typeof(IUserService)); 不是直接注入服务,而是注入容器容器本身也可以注入,它的类型是 IServiceProvider,这样,你可以自己来通过容器完成特殊的处理。 var sp = provider.GetService<IServiceProvider> (); var userService = sp.GetService<IUserService> (); Console.WriteLine (userService);
5 简化注册过程?https://www.cnblogs.com/catcher1994/p/handle-multi-implementations-with-same-interface-in-dotnet-core.html ? 参考资料:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- ruby-on-rails – 没有成员编号的资源路由
- xml矢量图 svg的viewBox和vml的coordsize,虚坐标系
- ruby-on-rails – 如何为ActiveAdmin上的某些操作添加范围
- metasploit3.6安装配置postgresql
- VB.net学习笔记(二)vb.net界面
- 正则表达式用MS Word中的另一个字符串替换字符串?
- ruby-on-rails – 我如何在我的机器上对Ruby突然这么慢的原
- ruby-on-rails – 找不到ID为primary的连接池
- c# – 从类中分解所有依赖关系的最简单,最快捷的方法
- net.sf.json.JSONException: Unquotted string "E44C2B