NopCommerce使用Autofac实现依赖注入
来源:互联网 发布:九年级下册历史书淘宝 编辑:程序博客网 时间:2024/04/20 07:11
NopCommerce使用Autofac实现依赖注入
文章内容:
- 什么是依赖注入
- NopCommerce网站中使用Autofac
什么是依赖注入
依赖注入也叫"控制反转(IOC)",目的在于将组件的配置与使用分离开来。
举个简单的例子:我饿了,要叫外卖吃,我只要打开APP,选好吃的,然后付钱,等着外卖到了就可以吃了。至于外卖员怎么送外卖,是步行送还是骑车送, 是走什么路线,这些我不需要知道,也无法知道。
我们从代码来看,理解起来会更清晰一点
首先,我给送餐定义了一个接口ITakeout:
//送餐接口,SendFood送餐的方法定义 public interface ITakeout { string SendFood() }
然后,订餐的实现类:
public class OnlineOrdering { private ITakeout _takeoutService; public string Ordering() { APP订餐付款(); //调用_takeoutService的SendFood方法获得食物food, _takeoutService的具体实现类我是不知道的。 string food = _takeoutService.SendFood(); return food; } }
在上面的例子中,虽然我知道有送餐这个过程,但是我并不知道它的具体实现方式,所以我不能new一个实例给_takeoutService属性。退一步讲,就算我知道一个类TakeoutByFoot实现了这个接口,于是我将它赋值给 _takeoutService, 将构造函数改为如下代码:
public OnlineOrdering(){ this._takeoutService=new TakeoutByFoot();}
这样做的问题是,如果明天换了个外卖员,他是骑车送餐的,后天又换了一个,他是开车送的。那我不是要频繁的更改OnlineOrdering类。这就造成了耦合。
依赖注入的好处在于我只要定义一个送餐的接口,然后就可以使用它,至于接口的实现类是什么我不需要知道,因为IOC框架会自动为接口赋值。将OnlineOrdering构造方法改为:
public OnlineOrdering(ITakeout takeoutService) { this._takeoutService = takeoutService; }
这样IOC框架就可以用构造子注入的方式,为 _takeoutService 添加具体实现类。至于是实现那个类,这个可以交给配置来做。如何配置这个后面会讲。这就是”使用”与 “配置”分离。
有人会说为什么不直接在外部给 _takeoutService 属性赋值,那样的话 _takeoutService 属性就必须是public ,可我并不想将这个属性暴露给外部。另外,如果ITakeout实现类中还定义了其他引用型属性,我就必须一层层赋值,这不是理想的方式。
NopCommerce网站中使用Autofac
官方网站地址
NopCommerce的官方网站,可以下载源码,详见[http://www.nopcommerce.com/][1].
Autofac的官方网站,详见[http://autofac.org/][1].
NopCommerce是国外的一个高质量的开源b2c网站系统。该网站使用了名为Autofac的依赖注入框架。Autofac的使用方法这里就不做介绍了,可以去官网上看,我只是简单分析一下NopCommerce是如何使用Autofac框架的。(本人水平有限,这只是我自己的理解,如果有不对的地方,欢迎指正)
代码在Nop.Core.Infrastructure和Nop.Core.Infrastructure.DependencyManagement命名空间。主要分为两个模块:
ContainerManager 的功能有:管理依赖注入的容器,实例化服务。
Engine 的功能有:管理ContainerManager, 实例化服务,在程序启动时注册各种服务。
另外还有一些工具模块,这里只简单说明。具体实现可以看代码:
首先,来看ContainerManager 类,部分代码:
public class ContainerManager { private readonly IContainer _container; public ContainerManager(IContainer container) { this._container = container; } public virtual IContainer Container { get { return this._container; } } //........各种Resolve方法,略 }
ContainerManager 中有个只读属性IContainer _container这是依赖注入容器,我们注册的服务都有保存到 _container中,同时实例化服务也是从 _container中获取。它相当于一个仓库。ContainerManager 通过在构造函数中传入IContainer 的实例,为 _container赋值。同时,ContainerManager 也提供了Resolve方法,为在 _container中注册的服务提供实例。
再来看下Engion模块,Engion模块定义了一个接口IEngine,该接口的实现类NopEngine,以及用来保存NopEngine单例的EngineContext。
IEngine定义如下:
public interface IEngine { ContainerManager ContainerManager { get; } void Initialize(NopConfig config); T Resolve<T>() where T : class; object Resolve(Type type); T[] ResolveAll<T>(); }
很清楚看到,IEngion有个ContainerManager 类型的属性,通过这个属性我们可以获得IContainer , 看一下NopEngine的代码就会发现ContainerManager 是在执行Initialize方法的时候赋值的。IEngion还提供了Resolve方法,其实就是调用ContainerManager 的Resolve方法,向外界提供服务。
IEngion的实现类NopEngine:
public class NopEngine : IEngine { private ContainerManager _containerManager; protected virtual void RegisterDependencies(NopConfig config) { // ① var builder = new ContainerBuilder(); var container = builder.Build(); this._containerManager = new ContainerManager(container); //dependencies WebAppTypeFinder是ITypeFinder的实现类,属于TypeFinder 模块。暂时知道它是干什么的就行了。 var typeFinder = new WebAppTypeFinder(); //因为ContainerBuilder只能Build()或Update()一次,所以要新建一个 builder = new ContainerBuilder(); // ② builder.RegisterInstance(this).As<IEngine>().SingleInstance(); builder.RegisterInstance(typeFinder).As<ITypeFinder>().SingleInstance(); builder.Update(container); //register dependencies provided by other assemblies ③ builder = new ContainerBuilder(); var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>(); var drInstances = new List<IDependencyRegistrar>(); foreach (var drType in drTypes) drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType)); //sort drInstances = drInstances.AsQueryable().OrderBy(t => t.Order).ToList(); foreach (var dependencyRegistrar in drInstances) dependencyRegistrar.Register(builder, typeFinder, config); builder.Update(container); //set dependency resolver ④ DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); } public void Initialize(NopConfig config) { //register dependencies RegisterDependencies(config); //.......执行启动任务,与依赖注入无关,略 } //.............Resolve方法,略 public ContainerManager ContainerManager { get { return _containerManager; } } }
以上代码我只截取了比较重要的部分,NopEngine类的重点在Initialize方法,该方法调用了RegisterDependencies方法,从方法名可以知道用来注册依赖项(服务)。
详细过程如下:
① build 了一个IContainer 容器,然后用该容器初始化ContainerManager ,以后注册的服务都将保存在这个容器中。由于NopEngine是单例,所以IContainer 容器也只有一个。
②将自己注册为一个单例,TypeFinder 模块也注册一个单例。注意builder.Update(container)这句一定要加,它用来将注册的服务保存到容器中去。
③首先来看这句代码var drTypes = typeFinder.FindClassesOfType<IDependencyRegistrar>();
这句的意思是找出所有IDependencyRegistrar接口的实现类。IDependencyRegistrar的定义如下:
public interface IDependencyRegistrar { void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config); int Order { get; } }
然后再看这两句代码:
1.foreach (var drType in drTypes) drInstances.Add((IDependencyRegistrar) Activator.CreateInstance(drType));2.foreach (var dependencyRegistrar in drInstances) dependencyRegistrar.Register(builder, typeFinder, config); builder.Update(container);
1表示实例化所有IDependencyRegistrar实现类,2表示执行IDependencyRegistrar实现类的Register方法,并且将builder作为参数传进去。然后将builder更新到container容器中。
这步操作有什么用呢? 举例一个IDependencyRegistrar的实现类:
public class DependencyRegistrar : IDependencyRegistrar { public void Register(ContainerBuilder builder, ITypeFinder finder ,FLOWConfig config) { builder.RegisterType<TakeoutByFoot>().As<ITakeout>().InstancePerLifetimeScope(); } public int Order { get { return 1; } } }
DependencyRegistrar 可以通过Register方法注册自己需要的服务。这样做的好处是,不同的服务使用者可以定义自己的IDependencyRegistrar实现类,然后注册自己需要的服务,这样服务使用起来非常方便,更换服务也很快捷。而服务提供者,我只要提供一个接口给使用者,至于使用者如何配置,我可以不用考虑。拿开始的例子说就是,外卖员如何送餐由他自己选择,而不是我来选择一个骑摩托的外卖员。
④set dependency resolver
NopEngine理解了以后,剩下就很好理解了。EngionContext提供了NopEngine的单例,代码如下:
public class EngineContext { #region Methods [MethodImpl(MethodImplOptions.Synchronized)] public static IEngine Initialize(bool forceRecreate) { if (Singleton<IEngine>.Instance == null || forceRecreate) { Singleton<IEngine>.Instance = new NopEngine(); var config = ConfigurationManager.GetSection("NopConfig") as NopConfig; Singleton<IEngine>.Instance.Initialize(config); } return Singleton<IEngine>.Instance; } public static void Replace(IEngine engine) { Singleton<IEngine>.Instance = engine; } #endregion #region Properties public static IEngine Current { get { if (Singleton<IEngine>.Instance == null) { Initialize(false); } return Singleton<IEngine>.Instance; } } #endregion }
可以看出,Singleton.Instance保存了IEngine的单例,Current用来获取IEngine单例。Initialize方法在IEngine单例为空或强制Recreate的情况下,初始化IEngine引擎。Singleton<IEngine>.Instance.Initialize(config);
这句代码表示IEngine引擎初始化,在初始化过程中,会注册服务。
最后,在程序启动时初始化IEngine引擎就会自动注册服务了:
protected void Application_Start() { ........ //initialize engine context EngineContext.Initialize(false); ....... }
- NopCommerce使用Autofac实现依赖注入
- C#使用AutoFac实现IOC依赖注入
- .NET 使用Autofac实现依赖注入
- 使用 Autofac 进行依赖注入
- Asp.mvc(三) ~ 使用 Autofac 实现依赖注入
- ASP.NET Core依赖注入解读&使用Autofac替代实现
- 依赖注入框架Autofac的简单使用
- 依赖注入框架Autofac的简单使用
- 依赖注入框架Autofac的简单使用
- 依赖注入框架Autofac的简单使用
- 依赖注入框架Autofac的简单使用
- 依赖注入框架Autofac的简单使用
- 依赖注入框架Autofac的简单使用
- 依赖注入框架Autofac的简单使用
- 依赖注入框架Autofac的简单使用
- 依赖注入框架Autofac的简单使用
- 依赖注入框架Autofac的简单使用
- 依赖注入之Autofac使用总结
- hdu4126Genghis Khan the Conqueror
- 用 GIT 把代码部署到服务器上
- 利用python找房子
- redis通过SLAVEOF命令进行主从配置
- 提高 Vim 和 Shell 效率的 9 个建议
- NopCommerce使用Autofac实现依赖注入
- 虚拟机中 yum 安装时报错
- css中em单位的用法
- 今日开始学习C++
- lucene源码分析---9
- 单链表的翻转
- TCP协议中的三次握手和四次握手
- 关于在浏览器上直接访问工程问题
- 树莓派用单个LED数码管显示当前环境温度