类型扫描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 找到所有 AttributeEntityAttribute 的类型,就能解决了 :

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<Type> entityTypes = args
.ScannedAttributeAndTypeInfos
.Where(x => x.Attribute is EntityAttribute)
.Select(x => x.Type);
}
}
}

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

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

4.1 App 上下文

AppAppSetup.Start() 得到的实例。

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

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

public override void OnUsing(AppModuleUsingArguments args)
{
IEnumerable<Type> entityTypes = args
.ScannedAttributeAndTypeInfos
.Where(x => x.Attribute is EntityAttribute)
.Select(x => x.Type);
args.AppSetup
.AppContext
.GetOrCreate<List<Type>>("EntityTypes", key =>
{
return new List<Type>();
})
.AddRange(entityTypes);
}

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

var app = AppSetup.Start<XXXAppModule>();
List<Type> entityTypes = app.Context
.GetOrCreate<List<Type>>("EntityTypes", key => new List<Type>());
// code-first here

4.2 AppContainerBuilder / AppContainer

AppContainerApp 对象的构成要素,

App 的本质只有两样

  • 字典类型的上下文
  • 所有 AppContainer

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

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

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

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

根据这个分门别类管理的思想,所有的实体类型也应当由专门的 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<Type> entityType; public EntityAppContainer(IEnumerable<Type> 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<Type> entityTypes; public EntityAppContainerBuilder()
{
entityTypes = new List<Type>();
} public void AddEntityType(Type type)
{
this.entityTypes.Add(type);
} public override IAppContainer Build(AppSetup setup)
{
return new EntityAppContainer(entityTypes);
}
}
}

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

最后的 Build 方法会由 AppSetup 内部执行。

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<EntityAppContainerBuilder>(); 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<DemoAppModule>();
Console.ReadLine();
}
}
}

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

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

文中项目代码可以从这里下载 : Reface.AppStarter.Demos.ScanEntities @ Github

Reface.AppStarter 类型扫描 —— 获得系统中所有的实体类型的更多相关文章

  1. MySQL 日期类型及默认设置 (除timestamp类型外,系统不支持其它时间类型字段设置默认值)

    MySQL 日期类型及默认设置 之前在用 MySQL 新建 table,创建日期类型列时遇到了一些问题,现在整理下来以供参考. MySQL 的日期类型如何设置当前时间为其默认值? 答:请使用 time ...

  2. 三、Linux系统中的文件类型和文件扩展名

    .sock文件也是一类特殊的文件,这类文件通常用在网络之间进行数据连接,如:我们可以启动一个程序来监听客户端的要求,客户端可以通过套接字来进行通信: linux中的文件类型 文件类型介绍 Linux系 ...

  3. Reface.AppStarter 基本示例

    Reface.AppStarter 向应用层提供以下几项 核心 功能 以模块化组织你的应用程序 自动注册组件至 IOC 容器 自动映射配置文件至配置类 在模块定义类中额外追加组件至 IOC 容器 在模 ...

  4. lvscan 查看系统中存在的所有LVM逻辑卷

    相关命令:lvresize,lvreduce,lvextend,lvdisplay,lvcreate,lvremove lvscan指令:扫描逻辑卷[语    法]lvscan [选项][功能介绍]l ...

  5. ASP.NET Web API路由系统:路由系统的几个核心类型

    虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分(定义在程序集System.Web.Http.dll中)已经移除 ...

  6. ASP.NET Web API框架揭秘:路由系统的几个核心类型

    ASP.NET Web API框架揭秘:路由系统的几个核心类型 虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分 ...

  7. Oracle中Blob和Clob类型的区别与操作

    Oracle中Blob和Clob类型 1.Oracle中Blob和Clob类型的区别 BLOB和CLOB都是大字段类型,BLOB是按二进制来存储的,而CLOB是可以直接存储文字的.其实两个是可以互换的 ...

  8. 使用typeid(变量或类型).name()来获取常量或变量的类型---gyy整理

    使用typeid(变量或类型).name()来获取常量或变量的类型 <typeinfo>  该头文件包含运行时类型识别(在执行时确定数据类型)的类 typeid的使用   typeid操作 ...

  9. Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作

    Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作 1>. 创建一个控制台程序2>. 添加一个 ADO.NET实体数据模型,选择对应的数据库与表(Studen ...

随机推荐

  1. VB.NET在基类中定义共享事件(类似于C#中的静态事件)

    基类: Public Class userFun Private Shared _PnlStatus As String ‘必须设为共享字段,如果不设为Shared,将不能传递字符串内容 Public ...

  2. Newtonsoft 六个超简单又实用的特性,值得一试 【上篇】

    一:讲故事 看完官方文档,阅读了一些 Newtonsoft 源码,对它有了新的认识,先总结 六个超经典又实用的特性,同大家一起分享,废话不多说,快来一起看看吧~~~ 二:特性分析 1. 代码格式化 如 ...

  3. Java 中的数据结构类 Stack

    JDK 中的 Stack 类便是经典的数据结构栈的实现,它继承于线程安全的 Vector 类,而且它自身的线程不安全的方法上也加上了 synchronized 关键字,所以它的内部操作也是线程安全的哦 ...

  4. MQ消息队列(2)—— Java消息服务接口(JMS)

    一.理解JMS   1.什么是JMS?         JMS即Java消息服务(Java Message Service)应用程序接口,API是一个消息服务的标准或者说是规范,允许应用程序组件基于J ...

  5. java基础 内部类详解

    什么是内部类? 1.内部类也是一个类: 2.内部类位于其他类声明内部. 内部类的常见类型 1.成员内部类 2.局部内部类 3.匿名内部类 4.静态内部类 简单示例 /** * 外部类 * */ pub ...

  6. android handle详解3 ThreadHandler

    在android handle详解2的基础上,我们来学习ThreadHandler ThreadHandler的本质就是对android handle详解2的实现 HandlerThread其实还是一 ...

  7. Jmeter系列(30)- 详解 JDBC Request

    如果你想从头学习Jmeter,可以看看这个系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html 前言 JDBC Request 主要是 ...

  8. JavaScript基础函数的属性:记忆模式(019)

    函数在Javascript里是有属性的,因为它们是一种特殊对象.事实上,就算是没有明确声明,函数在最初就已经包含了一些固有的属性,比如所有函数都length这个属性,它可以指出函数声明了多少个参数: ...

  9. 让对象拥有状态——C#中的状态模式

    大家好,老胡又在博客和大家见面了,在聊今天的主角之前,老胡先给大家讲一个以前发生的故事.   真实的故事 当老胡还是小胡的时候,跟随团队一起开发一款游戏.这款游戏是一款末日生存类游戏,玩家可以 收集资 ...

  10. 恕我直言你可能真的不会java第12篇-如何使用Stream API对Map类型元素排序

    在这篇文章中,您将学习如何使用Java对Map进行排序.前几日有位朋友面试遇到了这个问题,看似很简单的问题,但是如果不仔细研究一下也是很容易让人懵圈的面试题.所以我决定写这样一篇文章.在Java中,有 ...