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<Type> 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<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
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<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 类型扫描 —— 获得系统中所有的实体类型的更多相关文章
- MySQL 日期类型及默认设置 (除timestamp类型外,系统不支持其它时间类型字段设置默认值)
MySQL 日期类型及默认设置 之前在用 MySQL 新建 table,创建日期类型列时遇到了一些问题,现在整理下来以供参考. MySQL 的日期类型如何设置当前时间为其默认值? 答:请使用 time ...
- 三、Linux系统中的文件类型和文件扩展名
.sock文件也是一类特殊的文件,这类文件通常用在网络之间进行数据连接,如:我们可以启动一个程序来监听客户端的要求,客户端可以通过套接字来进行通信: linux中的文件类型 文件类型介绍 Linux系 ...
- Reface.AppStarter 基本示例
Reface.AppStarter 向应用层提供以下几项 核心 功能 以模块化组织你的应用程序 自动注册组件至 IOC 容器 自动映射配置文件至配置类 在模块定义类中额外追加组件至 IOC 容器 在模 ...
- lvscan 查看系统中存在的所有LVM逻辑卷
相关命令:lvresize,lvreduce,lvextend,lvdisplay,lvcreate,lvremove lvscan指令:扫描逻辑卷[语 法]lvscan [选项][功能介绍]l ...
- ASP.NET Web API路由系统:路由系统的几个核心类型
虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分(定义在程序集System.Web.Http.dll中)已经移除 ...
- ASP.NET Web API框架揭秘:路由系统的几个核心类型
ASP.NET Web API框架揭秘:路由系统的几个核心类型 虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分 ...
- Oracle中Blob和Clob类型的区别与操作
Oracle中Blob和Clob类型 1.Oracle中Blob和Clob类型的区别 BLOB和CLOB都是大字段类型,BLOB是按二进制来存储的,而CLOB是可以直接存储文字的.其实两个是可以互换的 ...
- 使用typeid(变量或类型).name()来获取常量或变量的类型---gyy整理
使用typeid(变量或类型).name()来获取常量或变量的类型 <typeinfo> 该头文件包含运行时类型识别(在执行时确定数据类型)的类 typeid的使用 typeid操作 ...
- Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作
Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作 1>. 创建一个控制台程序2>. 添加一个 ADO.NET实体数据模型,选择对应的数据库与表(Studen ...
随机推荐
- TCP和UDP的Socket编程实验
Linux Socket 函数库是从 Berkeley 大学开发的 BSD UNIX 系统中移植过来的.BSD Socket 接口是在众多 Unix 系统中被广泛支持的 TCP/IP 通信接口,Lin ...
- Android学习笔记上下文菜单
布局文件main_activity.xml <?xml version="1.0" encoding="utf-8"?> <RelativeL ...
- HTTP Request Smuggling 请求走私
参考文章 浅析HTTP走私攻击 SeeBug-协议层的攻击--HTTP请求走私 HTTP 走私漏洞分析 简单介绍 攻击者通过构造特殊结构的请求,干扰网站服务器对请求的处理,从而实现攻击目标 前提知识 ...
- 面试必问系列之JDK动态代理
.katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...
- 记一次mysql小版本升级
最近护网操作比较紧,基线和漏洞检查比较频繁,新扫描出来的mysql漏洞需要修复,没有啥好的修复方法,只剩下升级版本这一条路,生产环境是5.7.12,二进制文件直接解压使用的,看了一下现在最新的版本,5 ...
- c#,pagerank算法实现一
PageRank让链接来"投票" 一个页面的“得票数”由所有链向它的页面的重要性来决定,到一个页面的超链接相当于对该页投一票.一个页面的PageRank是由所有链向它的页面(“链入 ...
- [ C++ ] 勿在浮沙筑高台 —— 拾遗
explicit 主要用于处理一个参数的构造函数,使其不用于隐式类型转换(防止二义性) operator->() C++设计 ->可以一直保留下去 仿函数 仿函数会隐式继承他们中的一个(详 ...
- JavaWeb开发之四:servlet技术 黑马程序员_轻松掌握JavaWeb开发之四Servlet开发 方立勋老师视频教程相当的经典
总结: 记住:servlet对象在应用程序运行的过程中只创建一次,浏览器每次访问的时候,创建reponse对象 request对象,然后调用servlet的service方法,reponse对象和re ...
- 《T-GCN: A Temporal Graph Convolutional Network for Traffic Prediction》 论文解读
论文链接:https://arxiv.org/abs/1811.05320 最近发现博客好像会被CSDN和一些奇怪的野鸡网站爬下来?看见有人跟爬虫机器人单方面讨论问题我也蛮无奈的.总之原作者Misso ...
- 一文告诉你Linux如何配置KVM虚拟化--安装篇
KVM全称"Kernel-based Virtual Machine",即基于内核的虚拟机,在linux内启用kvm需要硬件,内核和软件(qemu)支持,这篇文章教你如何配置并安装 ...