Orchard详解--第三篇 依赖注入之基础设施
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 }
代码很长,但是可以分为以下几个部分:
- 基础组件:日志、事件、缓存
- 环境相关:路径、程序集、编译器,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 }
其实它的核心就是注册时通过对象类型查找是否有一个类型为Ilogger的可读写属性,拿出来赋值。