Reface.AppStarter 类型扫描 —— 获得系统中所有的实体类型

Reface.AppStarter 类型扫描 —— 获得系统中所有的实体类型

  类型扫描 是 Reface.AppStarter 提供的最基本、最核心的功能。

  AutoConfig , ComponentScan 等功能都是基于该功能完成的。

  每一个使用 Reface.AppStarter 的人都可以订制自己的扫描类型扫描逻辑。

  例如

  收集系统中所有的 实体 类型,并在系统启动后执行 Code-First 的相关操作。

  我们现在就以该示例为需求,开发一个能够 扫描实体,并借助第三方框架实现 CodeFirst 的示例程序。

  1. 创建程序

  创建一个名为 Reface.AppStarter.Demos.ScanEntities 的控制台项目用于承载我们的示例程序。

  2. 添加 Reface.AppStarter 的 nuget 依赖

  点击访问 Reface.AppStarter @ nuget 可以复制最新版本的 Reface.AppStarter 的命令行到 Package Manager 中。

  3. 创建专属的 Attribute

  在 Reface.AppStarter 对类型的扫描是通过 Attribute 识别的。

  Reface.AppStarter.Attributes.ScannableAttribute 表示该特征允许被 AppSetup 扫描并记录。

  因此只要将我们的 Attribute 继承于 ScannableAttribute 就可以被 AppSetup 扫描并记录。

  我们先创建一个目录 Attributes,并在该目录中创建特征类型 EntityAttribute 让其继承于 ScannableAttribute

  using Reface.AppStarter.Attributes;

  namespace Reface.AppStarter.Demos.ScanEntities.Attributes

  {

  public class EntityAttribute : ScannableAttribute

  {

  }

  }

  所有标记了 EntityAttribute 的类型都会被 AppSetup 发现、记录。

  我们使用这些记录就可以收集到系统内的所有实体,并进一步根据这些实体类型进行 Code-First 操作。

  4. 创建专属 AppModule

  Reface.AppStarter 中对模块的依赖和增加都是通过 AppModule 完成的。

  我们希望使用者添加对我们的 AppModule 依赖就可以扫描指定模块中的所有实体类型。

  在应用时,我们希望形如下面的代码:

  [ComponentScanAppModule] // IOC 组件扫描与注册模块

  [AutoConfigAppModule] // 自动配置模块

  [EntityScanAppModule] // 实体操作模块

  public class UserAppModule : AppModule

  {

  }

  很明显,我们需要创建一个名为 EntityScanAppModule 的类型。

  我们创建一个目录名为 AppModules,并将 EntityScanAppModule 创建在该目录下。

  此时,我们的目录如下

  - Reface.AppStarter.Demos.ScanEntities

  - AppModules

  EntityScanAppModule.cs

  - Attributes

  EntityAttribute.cs

  Program.cs

  此时的 EntityScanAppModule 是一个空白的 AppModule,

  还不具有找到所有标记了 EntityAttribute 的类型的能力。

  我们可以通过重写 OnUsing 方法赋予此功能。

  OnUsing 方法具备一个类型为 AppModuleUsingArguments 的参数。

  AppSetup , 此时启动过程的 AppSetup 实例

  TargetAppModule , 以之前的 UserAppModule 为例,TargetAppModule 就是 UserAppModule 的实例

  UsingAppModule , 以之前的 UserAppModule 为例,UsingAppModule 就是 EntityScanAppModule 的实例,也就是 this 指向的实例

  ScannedAttributeAndTypeInfos , 从 TargetAppModule 中扫描所到的全部类型信息

  看到最后一个属性,应该一切就简单了。

  从 ScannedAttributeAndTypeInfos 找到所有 Attribute 是 EntityAttribute 的类型,就能解决了 :

  using Reface.AppStarter.AppModules;

  using Reface.AppStarter.Demos.ScanEntities.Attributes;

  using System;

  using System.Collections.Generic;

  using System.Linq;

  namespace Reface.AppStarter.Demos.ScanEntities.AppModules

  {

  public class EntityScanAppModule : AppModule

  {

  public override void OnUsing(AppModuleUsingArguments args)

  {

  IEnumerable entityTypes = args

  .ScannedAttributeAndTypeInfos

  .Where(x => x.Attribute is EntityAttribute)

  .Select(x => x.Type);

  }

  }

  }

  我们现在需要考虑的是,如何将 entityTypes 保存下来,以便在系统启动后使用它们建表。

  除了使用 静态类 等常见手段保存这些信息,Reface.AppStarter 也提供了两种方式。

  4.1 App 上下文

  App 是 AppSetup.Start() 得到的实例。

  App.Context 属性是一个 Dictionary 对象,允许开发者自定义一些信息挂载在上下文中,以便在其它位置使用它们。

  AppSetup 在构建期间也可以预置一些信息到 App.Context 中。

  public override void OnUsing(AppModuleUsingArguments args)

  {

  IEnumerable entityTypes = args

  .ScannedAttributeAndTypeInfos

  .Where(x => x.Attribute is EntityAttribute)

  .Select(x => x.Type);

  args.AppSetup

  .AppContext

  .GetOrCreate>("EntityTypes", key =>

  {

  return new List();

  })

  .AddRange(entityTypes);

  }

  通过这种方式,当系统启动后,我们可以通过 App.Context 得到所有扫描到的实体类型:

  var app = AppSetup.Start();

  List entityTypes = app.Context

  .GetOrCreate>("EntityTypes", key => new List());

  // code-first here

  4.2 AppContainerBuilder / AppContainer

  AppContainer 是 App 对象的构成要素,

  App 的本质只有两样

  字典类型的上下文

  所有 AppContainer

  每一种 AppContainer 都会管理某一类的类型,

  而这些类型都是通过 ScannableAttribute 扫描得到的。

  比如,在 Reface.AppStarter 中,有负责 IOC 和负责 AutoConfig 的两个 AppContainer。

  它们分别对标有 Component 和 Config 的类型进行不同的管理和处理。

  根据这个分门别类管理的思想,所有的实体类型也应当由专门的 AppContainer 管理所有的 实体类型。

  5. 创建 EntityAppContainer

  创建目录 AppContainers ,

  并在目录内创建 EntityAppContainer ,

  EntityAppContainer 需要继承 BaseAppContainer ,

  添加构造函数,传入所有的 实体类型 ,

  重写 OnAppStarted 方法 , 实现 Code-First 功能。

  using Reface.AppStarter.AppContainers;

  using System;

  using System.Collections.Generic;

  namespace Reface.AppStarter.Demos.ScanEntities.AppContainers

  {

  public class EntityAppContainer : BaseAppContainer

  {

  // all entity type here

  private readonly IEnumerable entityType;

  public EntityAppContainer(IEnumerable entityType)

  {

  this.entityType = entityType;

  }

  public override void OnAppStarted(App app)

  {

  entityType.ForEach(x => Console.WriteLine("Do CODE-FIRST from type {0}", x));

  }

  }

  }

  很明显,没有 entityType ,我们无法直接构造出 EntityAppContainer 。

  Reface.AppStarter 要求所有的 AppContainer 都是通过 AppContainerBuilder 创建得到的。

  与 AppContainer 不同,AppContainerBuilder 是被托管在 AppSetup 实例中的,

  开发者可以通过 AppSetup 的实例 访问 指定类型的 AppContainerBuilder 。

  AppContainerBuilder 一旦被 访问 ,就会立刻创建,并在最终生成 App 实例时,构建成相应的 AppContainer 并移交给 App。

  6. 创建 EntityAppContainerBuilder

  创建目录 AppContainerBuilders,

  在目录内创建类型 EntityAppContainerBuilder 继承 BaseAppContainerBuilder,

  并重写 Build 方法。

  using Reface.AppStarter.AppContainerBuilders;

  using Reface.AppStarter.AppContainers;

  using System;

  namespace Reface.AppStarter.Demos.ScanEntities.AppContainerBuilders

  {

  public class EntityAppContainerBuilder : BaseAppContainerBuilder

  {

  public override IAppContainer Build(AppSetup setup)

  {

  throw new NotImplementedException();

  }

  }

  }

  很明显,我们知道需要在 Build 中写下这样的代码

  return new EntityAppContainer(entityTypes);

  entityTypes 从何而来?

  这个就简单了,为 EntityAppContainerBuilder 添加一个 AddEntityType(Type type) 就行了。

  using Reface.AppStarter.AppContainerBuilders;

  using Reface.AppStarter.AppContainers;

  using Reface.AppStarter.Demos.ScanEntities.AppContainers;

  using System;

  using System.Collections.Generic;

  namespace Reface.AppStarter.Demos.ScanEntities.AppContainerBuilders

  {

  public class EntityAppContainerBuilder : BaseAppContainerBuilder

  {

  private readonly ICollection entityTypes;

  public EntityAppContainerBuilder()

  {

  entityTypes = new List();

  }

  public void AddEntityType(Type type)

  {

  this.entityTypes.Add(type);

  }

  public override IAppContainer Build(AppSetup setup)

  {

  return new EntityAppContainer(entityTypes);

  }

  }

  }

  从这个类型不难发现,我们需要创建一个 EntityAppContainerBuilder ,并使用 AddEntityType 将实体类型加入。

  最后的 Build 方法会由 AppSetup 内部执行。郑州哪家男科医院好http://www.hnzzyyxb.com/

  7. 使用 EntityScanAppModule 操作 EntityAppContainerBuilder

  现在回到之前的 EntityScanAppModule ,

  从前向 App.Context 内预置信息的代码可以删除掉了。

  我们先从 AppSetup 中获取 EntityAppContainerBuilder 的实例,

  配合上 AddEntityType,然后就一气呵成了。

  using Reface.AppStarter.AppModules;

  using Reface.AppStarter.Demos.ScanEntities.AppContainerBuilders;

  using Reface.AppStarter.Demos.ScanEntities.Attributes;

  using System.Linq;

  namespace Reface.AppStarter.Demos.ScanEntities.AppModules

  {

  public class EntityScanAppModule : AppModule

  {

  public override void OnUsing(AppModuleUsingArguments args)

  {

  EntityAppContainerBuilder builder = args.AppSetup.GetAppContainerBuilder();

  args

  .ScannedAttributeAndTypeInfos

  .Where(x => x.Attribute is EntityAttribute)

  .Select(x => x.Type)

  .ForEach(x => builder.AddEntityType(x));

  }

  }

  }

  8. 准备我们的启动程序

  创建 DemoAppModule,

  添加一些 Entity,

  添加 EntityScanAppModule 的依赖,

  启动,即可测试我们的代码了。

  using Reface.AppStarter.AppModules;

  using Reface.AppStarter.Demos.ScanEntities.AppModules;

  namespace Reface.AppStarter.Demos.ScanEntities

  {

  [EntityScanAppModule]

  public class DemoAppModule : AppModule

  {

  }

  }

  在 Program.cs 中编写启动程序

  using System;

  namespace Reface.AppStarter.Demos.ScanEntities

  {

  class Program

  {

  static void Main(string[] args)

  {

  AppSetup.Start();

  Console.ReadLine();

  }

  }

  }

  控制台中就可以得到所有的实体类型

  Do CODE-FIRST from type Reface.AppStarter.Demos.ScanEntities.Entities.Role

  Do CODE-FIRST from type Reface.AppStarter.Demos.ScanEntities.Entities.User