SCSF 系列:Smart Client Software Factory 启动过程详解

应网友要求,结合参考实现(BankBranchWorkbench)写一篇关于 SCSF 内部工作原理的文章,需要读者有 SCSF 基础。基本概念和基本理念后面相关文章介绍。

SCSF 自动为我们建立了 Shell 项目。该项目的 ShellApplication 是SCSF 应用的入口程序,该类继承自 SmartClientApplication<TWorkItem, TShell> ,TWorkItem 是要指定的 root workitem ,TShell 是主窗体。

ShellApplication

该类的 Main 方法通过 new ShellApplication().Run(); 启动应用。Run() 在父类 CabApplication 中实现,定义了 SCSF 的启动流程:

1  public   void Run()
2  {
3      RegisterUnhandledExceptionHandler();
4      Builder builder = CreateBuilder();
5      AddBuilderStrategies(builder);
6      CreateRootWorkItem(builder);
7 
8      IVisualizer visualizer = CreateVisualizer();
9      if (visualizer !=   null )
10          visualizer.Initialize(rootWorkItem, builder);
11 
12      AddRequiredServices();
13      AddConfiguredServices();
14      AddServices();
15      AuthenticateUser();
16      ProcessShellAssembly();
17      rootWorkItem.BuildUp();
18      LoadModules();
19      rootWorkItem.FinishInitialization();
20 
21      rootWorkItem.Run();
22      Start();
23 
24      rootWorkItem.Dispose();
25      if (visualizer !=   null )
26          visualizer.Dispose();
27  }
28 

其中核心流程有:

1. 首先创建 Builder builder = CreateBuilder(); 

CreaterBuilder()方法注册了 RootWorkItemInitializationStrategy ,EventBrokerStrategy,CommandStrategy,ObjectBuiltNotificationStrategy 总共四个策略,同时还添加了三个 Policy :SingletonPolicy,BuilderTraceSourcePolicy,ObjectBuiltNotificationPolicy 。

CreaterBuilder() 一般使用 builder.Strategies.AddNew 方法利用 ObjectBuilder 构建策略对象 (例如:builder.Strategies.AddNew<EventBrokerStrategy>(BuilderStage.Initialization))。

2. 子类可以通过重写 protected virtual void AddBuilderStrategies(Builder builder) 来给 ObjectBuilder 添加其他构建策略(在构建对象或者销毁对象时执行的操作)。

3. 初始化 RootWorkItem

这些准备工作做完后,第一件事是创建 RootWorkItem 。RootWorkItem 是通过 CreateRootWorkItem(builder) 完成的,以前面创建的 builder 作为参数:protected internal void InitializeRootWorkItem(Builder builder)。

InitializeRootWorkItem 中首先初始化 RootWorkItem 相关的 Builder 和 Locator (这两个都是 ObjectBuilder 的组件,用与对象创建和依赖注入):

this .builder = builder;
this .locator =   new Locator();

其次对 rootWorkItem 进行初始化,主要执行以下三个方法:

InitializeFields():设置或初始化 ObjectBuilder 相关的对象: Builder,Locator,ObjectBuiltNotificationPolicy,还有 workItem 的状态。

InitializeState():通过 Guid 生成本 workItem 实例的 ID (ID = Guid.NewGuid().ToString(););

InitializeCollectionFacades():初始化管理 SCSF 核心组件的对象管理集合: ServiceCollection,commandCollection,workItemCollection,workspaceCollection,itemsCollection,smartPartCollection,eventTopicCollection,uiExtensionSiteCollection 。

RootWorkItem 构建完成后有一个可选的过程是创建 IVisualizer (用于在运行时查看 WorkItem 的状态):

1  IVisualizer visualizer = CreateVisualizer();
2  if (visualizer !=   null )
3      visualizer.Initialize(rootWorkItem, builder);

4. 添加服务:

AddRequiredServices() 方法添加的服务有:TraceSourceCatalogService,WorkItemExtensionService,WorkItemTypeCatalogService,SimpleWorkItemActivationService,WindowsPrincipalAuthenticationService,ModuleLoaderService,FileCatalogModuleEnumerator,DataProtectionCryptographyService,CommandAdapterMapService,UIElementAdapterFactoryCatalog 。

AddConfiguredServices() 添加在配置文件中配置的服务,也就是运行我们通过配置的方式决定在 SCSF 框架启动时加载额外服务(Services)。

AddServices() 在 CabApplication 中是一个空的虚拟方法,允许我们创建 CabApplication 的子类来重写该方法以添加需要的 Services 。

5. 验证用户

通过获取在启动过程中注册的 IAuthenticationService 服务来进行用户验证(ObjectBuilder 的具体应用):

1  private   void AuthenticateUser()
2  {
3      IAuthenticationService auth = rootWorkItem.Services.Get < IAuthenticationService > ( true );
4      auth.Authenticate();
5  }

6. 处理 Shell 程序集(可执行的 Shell Assembly)

1  private   void ProcessShellAssembly()
2  {
3      IModuleLoaderService loader = rootWorkItem.Services.Get < IModuleLoaderService > ( true );
4      Assembly assembly = Assembly.GetEntryAssembly();
5 
6      if (assembly !=   null )
7          loader.Load(rootWorkItem, assembly);
8  }
9 

通过 ObjectBuilder 获取已注册的 IModuleLoaderService (默认的是 Microsoft.Practices.CompositeUI.Services.ModuleLoaderService),调用 ModuleLoaderService 的 Load 方法来通过SCSF Attribute(反射和特性)结合 ObjectBuilder 来加载 SCSF 核心组件,包括 Services、 Command 、 SmartParts、UIElement、workspace、Event Broker、State 等,这些属性(Attribute)主要有:ServiceAttribute,ServiceDependencyAttribute,SmartPartAttribute,CommandHandlerAttribute,EventPublicationAttribute,EventSubscriptionAttribute,ModuleDependencyAttribute,StateChangedAttribute,RootWorkItemExtensionAttribute,OptionalDependencyAttribute,TraceSourceAttribute,ComponentDependencyAttribute,WorkItemExtensionAttribute ,以后有时间会专门介绍这些属性的使用。

7. 构建 RootWorkItem (rootWorkItem.BuildUp())

RootWorkItem 通过 builder.BuildUp(locator, type, temporaryID, this, policies) 方法使用 ObjectBuilder 进行构建,前面介绍过 CabApplication 在 CreaterBuilder() 中注册了构建策略 RootWorkItemInitializationStrategy: builder.Strategies.Add(new RootWorkItemInitializationStrategy(this.OnRootWorkItemInitialized), BuilderStage.Initialization),该语句表明在创建 RootWorkItem 时 ObjectBuilder 会执行该策略并调用本类(CabApplication 或者重载的子类)的 OnRootWorkItemInitialized 方法:protected virtual void OnRootWorkItemInitialized()。CabShellApplication (CabApplication 的子类) 重写了该方法:

1  protected   sealed   override   void OnRootWorkItemInitialized()
2  {
3      BeforeShellCreated();
4      shell = RootWorkItem.Items.AddNew < TShell > ();
5      AfterShellCreated();
6  }

其中 RootWorkItem.Items.AddNew<TShell>() 语句创建了新的 TShell 主窗体对象(通过 ObjectBuilder 构建)并加入到 RootWorkItem 的 Items 集合中。

8. 通过配置加载模块(LoadModules())

RootWorkItem 构建完成后,SCSF 会根据 IModuleEnumerator 服务来枚举可以加载的模块(确定需要加载哪些模块),SCSF 中提供了两个 IModuleEnumerator 服务:FileCatalogModuleEnumerator(在配置文件中指明要加载哪些模块,默认的配置文件是 ProfileCatalog.xml )和 ReflectionModuleEnumerator(利用反射和 ModuleAttribute 来确定需要加载哪些模块)。同时 SCSF 的Package Guidance 为我们提供了一个 XmlStreamDependentModuleEnumerator ,用于从 Xml Stream 中确定需要加载哪些模块,这个可以用在通过 Web Service 将服务器上的配置发送到客户端的情况。

典型的配置文件示例 ProfileCatalog.xml :

< SolutionProfile xmlns ="http://schemas.microsoft.com/pag/cab-profile/2.0%22>
  <Section Name="
Infrastructure" >
   
< Modules >
       
< ModuleInfo AssemblyFile ="GlobalBank.Support.Module.dll"   />  
       
< ModuleInfo AssemblyFile ="GlobalBank.Infrastructure.Module.dll"   />
   
</ Modules >
 
</ Section >
 
< Section Name ="BranchSystems" >
   
< Dependencies >
     
< Dependency Name ="Infrastructure"   />
   
</ Dependencies >
   
< Modules >
     
< ModuleInfo AssemblyFile ="GlobalBank.BranchSystems.Layout.dll" Name ="BranchSystems.Layout" />
     
< ModuleInfo AssemblyFile ="GlobalBank.BranchSystems.Module.dll" >
       
< Dependencies >
         
< Dependency Name ="BranchSystems.Layout"   />
       
</ Dependencies >
       
< Roles >
         
< Role Allow ="Greeter" />
         
< Role Allow ="Officer" />
         
< Role Allow ="BranchManager" />
       
</ Roles >
     
</ ModuleInfo >
   
</ Modules >
 
</ Section >
 
< Section Name ="LinesOfBusiness" >
   
< Dependencies >
     
< Dependency Name ="Infrastructure"   />
     
< Dependency Name ="BranchSystems"   />
   
</ Dependencies >
   
< Modules >
     
< ModuleInfo AssemblyFile ="GlobalBank.BasicAccounts.Module.dll" >
       
< Roles >
         
< Role Allow ="Officer" />
         
< Role Allow ="BranchManager" />
       
</ Roles >
     
</ ModuleInfo >
     
< ModuleInfo AssemblyFile ="GlobalBank.CreditCardAccounts.Module.dll" >
       
< Roles >
         
< Role Allow ="Officer" />
         
< Role Allow ="BranchManager" />
       
</ Roles >
     
</ ModuleInfo >
   
</ Modules >
 
</ Section >
</ SolutionProfile >  


确定要加载那些模块后,就会执行上面 “6. 处理 Shell 程序集”中的过程,利用注册的 IModuleLoaderService 加载模块。SCSF 规定每个 Module 程序集都有一个继承自 ModuleInit 的子类,IModuleLoaderService  会在加载完该 Module 后自动调用其 void Load() 方法,这里是 SCSF 为我们通过的接入点,我们应该在 void Load() 方法中初始化本模块。具体应该初始化什么以后介绍。 

9. 完成对 RootWorkItem 的创建(rootWorkItem.FinishInitialization())
主要是处理 workItem 扩展(以后介绍),并触发创建完成事件:

1  protected   internal   void FinishInitialization()
2  {
3      IWorkItemExtensionService extensionsService = Services.Get < IWorkItemExtensionService > ();
4      if (extensionsService !=   null )
5          extensionsService.InitializeExtensions( this );
6 
7      OnInitialized();
8  }
9 

同时 WorkItem 的 public void InitializeWorkItem() 是一个注入方法,有[InjectionMethod]属性标记,该方法执行时除了执行上面 rootworkItem中的初始化过程外,还会调用 InitializeServices 方法,该方法在 CabApplication 中是空方法,CabApplication 子类可以重写 protected virtual void InitializeServices() 方法,用于在 WorkItem 初始化时加载其他需要的服务。

10. 执行 rootWorkItem 的 run 方法 rootWorkItem.Run();

WorkItem 的 Run 方法直接调用 protected virtual void OnRunStarted() 方法:

1  public   void Run()
2  {
3      OnRunStarted();
4  }

OnRunStarted 方法触发 RunStarted 事件(public event EventHandler RunStarted)

1  protected   virtual   void OnRunStarted()
2  {
3      if ( this .RunStarted !=   null )
4      {
5          this .RunStarted( this , EventArgs.Empty);
6      }
7 
8      if (traceSource !=   null )
9          traceSource.TraceInformation(String.Format(
10              CultureInfo.CurrentCulture,
11                    Properties.Resources.TraceWorkItemRunStarted, ID));
12  }
13 

因此我们可以通过注册 RunStarted 事件或者在 WorkItem 子类中重写 OnRunStarted() 虚方法以便在 SCSF 启动过程中执行自己的操作,这是 SCSF 的又一扩展点。

11. 启动应用 Start()

CabApplication 中的 Start 是一个抽象方法:protected abstract void Start();

子类 FormShellApplication(public abstract class FormShellApplication<TWorkItem, TShell> : WindowsFormsApplication<TWorkItem, TShell>) 重写了Start :

1  protected   override   void Start()
2  {
3      Application.Run(Shell);
4  }

很简单,这时主窗体就显示出来了,以后的整个操作就是通过用户交互来触发了。

以上只是简单的介绍了一下 SCSF 的启动流程,还有很多细节后面的文章会接受。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值