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 ...
随机推荐
- C# 9.0 新特性之目标类型推导 new 表达式
阅读本文大概需要 2 分钟. 呼~~,每次过完一个周末,写作就失去了动力,一两天才能缓过来.尽管如此,还是要坚持写好每一篇文章的.宁缺毋滥嘛,宁愿发文的频率低一点,也要保证文章的质量,至少排版不能差, ...
- Tensorflow2 自定义数据集图片完成图片分类任务
对于自定义数据集的图片任务,通用流程一般分为以下几个步骤: Load data Train-Val-Test Build model Transfer Learning 其中大部分精力会花在数据的准备 ...
- opencv C++图像读取
int main(){ cv::Mat img=cv::imread("/home/nan/图片/highdeepth/starry.jpg",cv::IMREAD_REDUCED ...
- 47 _ 循环队列程序演示.swf
通过上面的分析我们已经对循环队列很了解了,现在我们来学习下循环队列的实现形式 1.代码使用数组现实循环队列 #include<stdio.h> #include<malloc.h&g ...
- android 抓取native层奔溃
使用android的breakpad工具 使用这个工具需要下载Breakpad的源码,然后进行编译,编译之后会生成两个工具 我们使用这两个工具来解析奔溃的位置.这里我们可以下载已经编译好的工具 下载地 ...
- 小师妹学JVM之:JIT中的PrintCompilation
目录 简介 PrintCompilation 分析PrintCompilation的结果 总结 简介 上篇文章我们讲到了JIT中的LogCompilation,将编译的日志都收集起来,存到日志文件里面 ...
- Spring Boot2+Resilience4j实现容错之Bulkhead
Resilience4j是一个轻量级.易于使用的容错库,其灵感来自Netflix Hystrix,但专为Java 8和函数式编程设计.轻量级,因为库只使用Vavr,它没有任何其他外部库依赖项.相比之下 ...
- robot framework使用小结(三)
robot framework采用行为驱动 新建测试案例baidu04,添加Library:Selenium2Library 右键项目名robotProject-->New Resource-- ...
- 1166 - Unknown error 1166[mysql 错误
错误码 1166 原因 字段名因为是复制过来的, 末尾存在了一个空格换行
- .Net Core 集成ExceptionLess分布式日志框架之本地化部署
前言 公司目前使用的项目中关于日志记录这块,之前一直都是使用的Log4net 存放于后台文件中的,对于异常错误啊,或者需要查看一些详情错误的时候感觉很不方便,要到服务器上去打开日志文件检索错误,降低了 ...