Orchard提供了依赖注入机制,并且框架的实现也离不开依赖注入如模块管理、日志、事件等。在前一篇中提到在Global.asax中定义的HostInitialization创建了Autofac的IoC容器。
1 private static IOrchardHost HostInitialization(HttpApplication application) {
2 var host = OrchardStarter.CreateHost(MvcSingletons);
3
4 host.Initialize();
5
6 // initialize shells to speed up the first dynamic query
7 host.BeginRequest();
8 host.EndRequest();
9
10 return host;
11 }
其中OrchardStarter是位于Orchard.Framework项目中Environment目录下的类型
1 public static IOrchardHost CreateHost(Action<ContainerBuilder> registrations) {
2 var container = CreateHostContainer(registrations);
3 return container.Resolve<IOrchardHost>();
4 }
以下是容器创建的全量代码:
1 public static IContainer CreateHostContainer(Action<ContainerBuilder> registrations) {
2 ExtensionLocations extensionLocations = new ExtensionLocations();
3
4 var builder = new ContainerBuilder();
5 // Application paths and parameters
6 builder.RegisterInstance(extensionLocations);
7
8 builder.RegisterModule(new CollectionOrderModule());
9 builder.RegisterModule(new LoggingModule());
10 builder.RegisterModule(new EventsModule());
11 builder.RegisterModule(new CacheModule());
12
13 // a single default host implementation is needed for bootstrapping a web app domain
14 builder.RegisterType<DefaultOrchardEventBus>().As<IEventBus>().SingleInstance();
15 builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance();
16 builder.RegisterType<DefaultCacheContextAccessor>().As<ICacheContextAccessor>().SingleInstance();
17 builder.RegisterType<DefaultParallelCacheContext>().As<IParallelCacheContext>().SingleInstance();
18 builder.RegisterType<DefaultAsyncTokenProvider>().As<IAsyncTokenProvider>().SingleInstance();
19 builder.RegisterType<DefaultHostEnvironment>().As<IHostEnvironment>().SingleInstance();
20 builder.RegisterType<DefaultHostLocalRestart>().As<IHostLocalRestart>().Named<IEventHandler>(typeof(IShellSettingsManagerEventHandler).Name).SingleInstance();
21 builder.RegisterType<DefaultBuildManager>().As<IBuildManager>().SingleInstance();
22 builder.RegisterType<DynamicModuleVirtualPathProvider>().As<ICustomVirtualPathProvider>().SingleInstance();
23 builder.RegisterType<AppDataFolderRoot>().As<IAppDataFolderRoot>().SingleInstance();
24 builder.RegisterType<DefaultExtensionCompiler>().As<IExtensionCompiler>().SingleInstance();
25 builder.RegisterType<DefaultRazorCompilationEvents>().As<IRazorCompilationEvents>().SingleInstance();
26 builder.RegisterType<DefaultProjectFileParser>().As<IProjectFileParser>().SingleInstance();
27 builder.RegisterType<DefaultAssemblyLoader>().As<IAssemblyLoader>().SingleInstance();
28 builder.RegisterType<AppDomainAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
29 builder.RegisterType<GacAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
30 builder.RegisterType<OrchardFrameworkAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
31 builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().InstancePerDependency();
32 builder.RegisterType<ViewsBackgroundCompilation>().As<IViewsBackgroundCompilation>().SingleInstance();
33 builder.RegisterType<DefaultExceptionPolicy>().As<IExceptionPolicy>().SingleInstance();
34 builder.RegisterType<DefaultCriticalErrorProvider>().As<ICriticalErrorProvider>().SingleInstance();
35 //builder.RegisterType<RazorTemplateCache>().As<IRazorTemplateProvider>().SingleInstance();
36
37 RegisterVolatileProvider<WebSiteFolder, IWebSiteFolder>(builder);
38 RegisterVolatileProvider<AppDataFolder, IAppDataFolder>(builder);
39 RegisterVolatileProvider<DefaultLockFileManager, ILockFileManager>(builder);
40 RegisterVolatileProvider<Clock, IClock>(builder);
41 RegisterVolatileProvider<DefaultDependenciesFolder, IDependenciesFolder>(builder);
42 RegisterVolatileProvider<DefaultExtensionDependenciesManager, IExtensionDependenciesManager>(builder);
43 RegisterVolatileProvider<DefaultAssemblyProbingFolder, IAssemblyProbingFolder>(builder);
44 RegisterVolatileProvider<DefaultVirtualPathMonitor, IVirtualPathMonitor>(builder);
45 RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder);
46
47 builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>()
48 .Named<IEventHandler>(typeof(IShellSettingsManagerEventHandler).Name)
49 .Named<IEventHandler>(typeof(IShellDescriptorManagerEventHandler).Name)
50 .SingleInstance();
51 {
52 builder.RegisterType<ShellSettingsManager>().As<IShellSettingsManager>().SingleInstance();
53
54 builder.RegisterType<ShellContextFactory>().As<IShellContextFactory>().SingleInstance();
55 {
56 builder.RegisterType<ShellDescriptorCache>().As<IShellDescriptorCache>().SingleInstance();
57
58 builder.RegisterType<CompositionStrategy>().As<ICompositionStrategy>().SingleInstance();
59 {
60 builder.RegisterType<ShellContainerRegistrations>().As<IShellContainerRegistrations>().SingleInstance();
61 builder.RegisterType<ExtensionLoaderCoordinator>().As<IExtensionLoaderCoordinator>().SingleInstance();
62 builder.RegisterType<ExtensionMonitoringCoordinator>().As<IExtensionMonitoringCoordinator>().SingleInstance();
63 builder.RegisterType<ExtensionManager>().As<IExtensionManager>().SingleInstance();
64 {
65 builder.RegisterType<ExtensionHarvester>().As<IExtensionHarvester>().SingleInstance();
66 builder.RegisterType<ModuleFolders>().As<IExtensionFolders>().SingleInstance()
67 .WithParameter(new NamedParameter("paths", extensionLocations.ModuleLocations));
68 builder.RegisterType<CoreModuleFolders>().As<IExtensionFolders>().SingleInstance()
69 .WithParameter(new NamedParameter("paths", extensionLocations.CoreLocations));
70 builder.RegisterType<ThemeFolders>().As<IExtensionFolders>().SingleInstance()
71 .WithParameter(new NamedParameter("paths", extensionLocations.ThemeLocations));
72
73 builder.RegisterType<CoreExtensionLoader>().As<IExtensionLoader>().SingleInstance();
74 builder.RegisterType<ReferencedExtensionLoader>().As<IExtensionLoader>().SingleInstance();
75 builder.RegisterType<PrecompiledExtensionLoader>().As<IExtensionLoader>().SingleInstance();
76 builder.RegisterType<DynamicExtensionLoader>().As<IExtensionLoader>().SingleInstance();
77 builder.RegisterType<RawThemeExtensionLoader>().As<IExtensionLoader>().SingleInstance();
78 }
79 }
80
81 builder.RegisterType<ShellContainerFactory>().As<IShellContainerFactory>().SingleInstance();
82 }
83
84 builder.RegisterType<DefaultProcessingEngine>().As<IProcessingEngine>().SingleInstance();
85 }
86
87 builder.RegisterType<RunningShellTable>().As<IRunningShellTable>().SingleInstance();
88 builder.RegisterType<DefaultOrchardShell>().As<IOrchardShell>().InstancePerMatchingLifetimeScope("shell");
89 builder.RegisterType<SessionConfigurationCache>().As<ISessionConfigurationCache>().InstancePerMatchingLifetimeScope("shell");
90
91 registrations(builder);
92
93 var autofacSection = ConfigurationManager.GetSection(ConfigurationSettingsReaderConstants.DefaultSectionName);
94 if (autofacSection != null)
95 builder.RegisterModule(new ConfigurationSettingsReader());
96
97 var optionalHostConfig = HostingEnvironment.MapPath("~/Config/Host.config");
98 if (File.Exists(optionalHostConfig))
99 builder.RegisterModule(new ConfigurationSettingsReader(ConfigurationSettingsReaderConstants.DefaultSectionName, optionalHostConfig));
100
101 var optionalComponentsConfig = HostingEnvironment.MapPath("~/Config/HostComponents.config");
102 if (File.Exists(optionalComponentsConfig))
103 builder.RegisterModule(new HostComponentsConfigModule(optionalComponentsConfig));
104
105 var container = builder.Build();
106
107 //
108 // Register Virtual Path Providers
109 //
110 if (HostingEnvironment.IsHosted) {
111 foreach (var vpp in container.Resolve<IEnumerable<ICustomVirtualPathProvider>>()) {
112 HostingEnvironment.RegisterVirtualPathProvider(vpp.Instance);
113 }
114 }
115
116 ControllerBuilder.Current.SetControllerFactory(new OrchardControllerFactory());
117 FilterProviders.Providers.Add(new OrchardFilterProvider());
118
119 GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new DefaultOrchardWebApiHttpControllerSelector(GlobalConfiguration.Configuration));
120 GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new DefaultOrchardWebApiHttpControllerActivator(GlobalConfiguration.Configuration));
121 GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
122
123 GlobalConfiguration.Configuration.Filters.Add(new OrchardApiActionFilterDispatcher());
124 GlobalConfiguration.Configuration.Filters.Add(new OrchardApiExceptionFilterDispatcher());
125 GlobalConfiguration.Configuration.Filters.Add(new OrchardApiAuthorizationFilterDispatcher());
126
127 ViewEngines.Engines.Clear();
128 ViewEngines.Engines.Add(new ThemeAwareViewEngineShim());
129
130 var hostContainer = new DefaultOrchardHostContainer(container);
131 //MvcServiceLocator.SetCurrent(hostContainer);
132 OrchardHostContainerRegistry.RegisterHostContainer(hostContainer);
133
134 // Register localized data annotations
135 ModelValidatorProviders.Providers.Clear();
136 ModelValidatorProviders.Providers.Add(new LocalizedModelValidatorProvider());
137
138 return container;
139 }
View Code
代码很长,但是可以分为以下几个部分:
- 基础组件:日志、事件、缓存
- 环境相关:路径、程序集、编译器,Http上下文
- 异常处理:异常策略
- 拓展相关:拓展目录、拓展依赖、拓展管理。
- 多租户相关:Shell表、Shell启动。
- 拓展的依赖注入:拓展模块中的自定义注入、Host及HostComponents配置文件的注入。
- MVC相关:ControllerFactory替换,过滤器加载
- WebAPI相关:Selector、Activator替换,DependencyResolver替换、过滤器加载
- ViewEngine:使用ThemeAwareViewEngineShim。
- Model相关:使用LocalizedModelValidatorProvider作为Model验证器。
相对特殊的地方有:
- 日志的自动属性注入:
Orchard使用Autofac实现了日志的属性自动注入,具体代码如下:
1 protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) {
2 var implementationType = registration.Activator.LimitType;
3
4 // build an array of actions on this type to assign loggers to member properties
5 var injectors = BuildLoggerInjectors(implementationType).ToArray();
6
7 // if there are no logger properties, there's no reason to hook the activated event
8 if (!injectors.Any())
9 return;
10
11 // otherwise, whan an instance of this component is activated, inject the loggers on the instance
12 registration.Activated += (s, e) => {
13 foreach (var injector in injectors)
14 injector(e.Context, e.Instance);
15 };
16 }
17
18 private IEnumerable<Action<IComponentContext, object>> BuildLoggerInjectors(Type componentType) {
19 // Look for settable properties of type "ILogger"
20 var loggerProperties = componentType
21 .GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance)
22 .Select(p => new {
23 PropertyInfo = p,
24 p.PropertyType,
25 IndexParameters = p.GetIndexParameters(),
26 Accessors = p.GetAccessors(false)
27 })
28 .Where(x => x.PropertyType == typeof(ILogger)) // must be a logger
29 .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer
30 .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)); //must have get/set, or only set
31
32 // Return an array of actions that resolve a logger and assign the property
33 foreach (var entry in loggerProperties) {
34 var propertyInfo = entry.PropertyInfo;
35
36 yield return (ctx, instance) => {
37 string component = componentType.ToString();
38 if (component != instance.GetType().ToString()) {
39 return;
40 }
41 var logger = _loggerCache.GetOrAdd(component, key => ctx.Resolve<ILogger>(new TypedParameter(typeof(Type), componentType)));
42 propertyInfo.SetValue(instance, logger, null);
43 };
44 }
45 }
View Code
其实它的核心就是注册时通过对象类型查找是否有一个类型为Ilogger的可读写属性,拿出来赋值。
2. 通过动态代理实现的事件机制:
在.Net中提到事件就会想到委托,但是Orchard的事件比较特殊,它将事件定义为一个接口(实现IEventHandler的接口),对于生产者(事件发布者)它仅需要通过Orchard的容器注入一个相应接口类型即可,而观察者(事件的真实处理器)则仅需要实现相应接口即可(其余框架会自动处理)。
对.Net的委托不了解的可参考张子阳老师的博客:www.cnblogs.com/JimmyZhang/…
Orchard事件的使用如下(使用张子阳老师例子改了一下):
1 public interface IBoilHandler : IEventHandler
2 {
3 void OnTemperatureChanged(int temperature);
4 }
5
6 // 热水器
7 public class Heater
8 {
9 private int temperature;
10 private IBoilHandler _boilhandler;
11
12 public Heater(IBoilHandler boilhandler)
13 {
14 _boilhandler = boilhandler;
15 }
16
17 // 烧水
18 public void BoilWater()
19 {
20 for (int i = 0; i <= 100; i++)
21 {
22 temperature = i;
23
24 if (temperature > 95)
25 {
26 if (_boilhandler != null)
27 { //如果有对象注册
28 _boilhandler.OnTemperatureChanged(temperature); //调用所有注册对象的方法
29 }
30 }
31 }
32 }
33 }
34
35 // 警报器
36 public class Alarm : IBoilHandler
37 {
38 public void OnTemperatureChanged(int temperature)
39 {
40 Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", temperature);
41 }
42 }
43
44 // 显示器
45 public class Display : IBoilHandler
46 {
47 public void OnTemperatureChanged(int temperature)
48 {
49 Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", temperature);
50 }
51 }
那么这是如何实现的呢?
两个要点:
1. 在注册服务时将所有通过继承IEventhandler的子接口(如上面代码的IBoilHandler)通过动态代理,将相应接口的方法执行全部拦截并转到EventBus上,调用Notify方法,其参数为当前接口名称+方法名称和相应的参数。--生产者
1 public class EventsRegistrationSource : IRegistrationSource {
2 private readonly DefaultProxyBuilder _proxyBuilder;
3
4 public EventsRegistrationSource() {
5 _proxyBuilder = new DefaultProxyBuilder();
6 }
7
8 public bool IsAdapterForIndividualComponents {
9 get { return false; }
10 }
11
12 public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor) {
13 var serviceWithType = service as IServiceWithType;
14 if (serviceWithType == null)
15 yield break;
16
17 var serviceType = serviceWithType.ServiceType;
18 if (!serviceType.IsInterface || !typeof(IEventHandler).IsAssignableFrom(serviceType) || serviceType == typeof(IEventHandler))
19 yield break;
20
21 var interfaceProxyType = _proxyBuilder.CreateInterfaceProxyTypeWithoutTarget(
22 serviceType,
23 new Type[0],
24 ProxyGenerationOptions.Default);
25
26
27 var rb = RegistrationBuilder
28 .ForDelegate((ctx, parameters) => {
29 var interceptors = new IInterceptor[] { new EventsInterceptor(ctx.Resolve<IEventBus>()) };
30 var args = new object[] { interceptors, null };
31 return Activator.CreateInstance(interfaceProxyType, args);
32 })
33 .As(service);
34
35 yield return rb.CreateRegistration();
36 }
37 }
38
39 public class EventsInterceptor : IInterceptor {
40 private readonly IEventBus _eventBus;
41 private static readonly ConcurrentDictionary<Type,MethodInfo> _enumerableOfTypeTDictionary = new ConcurrentDictionary<Type, MethodInfo>();
42
43 public EventsInterceptor(IEventBus eventBus) {
44 _eventBus = eventBus;
45 }
46
47 public void Intercept(IInvocation invocation) {
48 var interfaceName = invocation.Method.DeclaringType.Name;
49 var methodName = invocation.Method.Name;
50
51 var data = invocation.Method.GetParameters()
52 .Select((parameter, index) => new { parameter.Name, Value = invocation.Arguments[index] })
53 .ToDictionary(kv => kv.Name, kv => kv.Value);
54
55 var results = _eventBus.Notify(interfaceName + "." + methodName, data);
56
57 invocation.ReturnValue = Adjust(results, invocation.Method.ReturnType);
58 }
59
60 public static object Adjust(IEnumerable results, Type returnType) {
61 if (returnType == typeof(void) ||
62 results == null ||
63 results.GetType() == returnType) {
64 return results;
65 }
66
67 // acquire method:
68 // static IEnumerable<T> IEnumerable.OfType<T>(this IEnumerable source)
69 // where T is from returnType's IEnumerable<T>
70 var enumerableOfTypeT = _enumerableOfTypeTDictionary.GetOrAdd( returnType, type => typeof(Enumerable).GetGenericMethod("OfType", type.GetGenericArguments(), new[] { typeof(IEnumerable) }, typeof(IEnumerable<>)));
71 return enumerableOfTypeT.Invoke(null, new[] { results });
72 }
73 }
View Code
上面代码有个重点就是:
1 if (!serviceType.IsInterface || !typeof(IEventHandler).IsAssignableFrom(serviceType) || serviceType == typeof(IEventHandler))
2 yield break;
如果不是接口、如果类型不是IEventHandler的子类型(子接口)且当前类型就是IEventHandler,满足这三个接口就跳出。正过来说就是:
- 类型一定是接口
- 一定是继承IEventHandler的接口
- 一定不是IEventHandler本身
2. 把所有实现了IEventHandler的实现全部解析到EventBus的_eventHandlers字段中。 --消费者
换句话说就是,当应用程序初始化的时候EventBus里面的处理器都已经被加载了,但真正触发事件时,仅仅是通过拦截器将相应接口的方法转到了Notify方法上,进而去通知相应的Handlers执行。
下面代码位于ShellContainerFactory.cs文件中
1 if (typeof(IEventHandler).IsAssignableFrom(item.Type)) {
2 var interfaces = item.Type.GetInterfaces();
3 foreach (var interfaceType in interfaces) {
4
5 // register named instance for each interface, for efficient filtering inside event bus
6 // IEventHandler derived classes only
7 if (interfaceType.GetInterface(typeof (IEventHandler).Name) != null) {
8 registration = registration.Named<IEventHandler>(interfaceType.Name);
9 }
10 }
11 }
3. ICacheManager的注入:
ICacheManager的特殊之处就在于依赖它的对象在通过构造注入ICacheManager会将当前对象作为参数来实例化ICacheManager。
1 protected override void AttachToComponentRegistration(Autofac.Core.IComponentRegistry componentRegistry, Autofac.Core.IComponentRegistration registration) {
2 var needsCacheManager = registration.Activator.LimitType
3 .GetConstructors()
4 .Any(x => x.GetParameters()
5 .Any(xx => xx.ParameterType == typeof(ICacheManager)));
6
7 if (needsCacheManager) {
8 registration.Preparing += (sender, e) => {
9 var parameter = new TypedParameter(
10 typeof(ICacheManager),
11 e.Context.Resolve<ICacheManager>(new TypedParameter(typeof(Type), registration.Activator.LimitType)));
12 e.Parameters = e.Parameters.Concat(new[] { parameter });
13 };
14 }
15 }
小结:
本章主要是分析了Orchard中IoC容器的创建以及基础设施的注册,其中对Log、Event和CacheManager进行了特殊处理,并且处理方式均是使用Autofac提供的一系列特性,动态的对注册的组件进行额外操作,另外在Orchard中它还针对Shell级别创建了一个子容器,所以后续将对Orchard的依赖注入进一步分析。
参考: