Reface.AppStarter 向应用层提供以下几项 核心 功能

  • 以模块化组织你的应用程序
  • 自动注册组件至 IOC 容器
  • 自动映射配置文件至配置类
  • 在模块定义类中额外追加组件至 IOC 容器
  • 在模块定义类中额外追加配置类
  • 事件总线

这些功能允许让开发者将功能拆分至各个小粒度模块,

当使用某个模块时,只需要向模块添加一个依赖,即可开启所有功能,

实现了整个模块的开箱即用。


本文将介绍 Reface.AppStarter 中最常用的三个功能 自动注册组件至 IOC 容器自动映射配置文件模块化

自动注册组件至 IOC 容器

使用 IOC 容器 往往分为两个阶段

  • 配置阶段
  • 使用阶段

配置阶段 我们可能会选择 配置文件对所有依赖的程序集反射对指定程序反射硬编码 等方式对组件进行 注册

对所有依赖的程序集反射 ,怎么看都是一种即笨重又有些呆板的方法。

然而另外几个方案在多模块化的项目里中 ,

也无法很好的工作,

它们各自需要在系统启动时,明确一些信息 :

  • 一共需要读取哪些配置文件,这些文件在各个子模块的哪儿
  • 一共需要对哪些程序集进行反射
  • 那些负责硬编码注册的类型都在哪儿

如果上述的信息没有被收集完整,那么程序会因为没有注册全部组件而无法正确运行。


事实上

在大多数情况下,开发者在编写一个 接口 和一个 实现 时,就已经确定 注册关系

只有那些可能面临 策略模式 ,或者存在多客户端适配的情况下,这种 注册关系 无法确定。

所以通过 Attribute 加 扫描,是一种很好的 自动化注册 手段。

假定我们需要实现下面的接口

// Services/IUserService.cs
public interface IUserService
{
void CreateUser(User user);
}

实现过程大致如下

// Services/DefaultUserService.cs
public class DefaultUserService : IUserService
{
private readonly IRepository<User> userRepo ; public DefaultUserService(IRepository<User> userRepo)
{
this.userRepo = userRepo;
} public void CreateUser(User user)
{
this.userRepo.Insert(user);
}
}

使用 Reface.AppStarter ,你不再需要编写额外的代码将这个 DefaultUserService 注册到 IUserService 上。

你只需要为 DefaultUserService 添加一个 ComponentAttribute

// Services/DefaultUserService.cs
[Component]
public class DefaultUserService : IUserService
{
// ...
}

这个 特征 就是通知 Reface.AppStarter 将该类型注册到 IOC 容器 中,并注册到它所有实现的接口类型上。


在由 Reface.AppStarter 构建的系统中,程序不是单纯的由 程序集 组成,而是由 应用模块 组成。

应用模块 不仅包含了 程序集 原有的功能,还能够体系与其它 应用模块 的依赖关系。

现在我们为刚才 DefaultUserService 所在的 程序集 编写一个 应用模块 的类型。

// UserAppModule.cs
[ComponentScanAppModule]
public class UserAppModule : AppModule
{ }

这就是一个 用户应用模块 ( 所有的 应用模块 应当从 AppModule 继承 ),

它依赖了一个 ComponentScanAppModule应用模块

ComponentScanAppModule 会将 目标应用模块 中标记了 ComponentAttribute 的类型注册到 IOC 容器中。

运行程序

public static void Main(String[] args)
{
var app = AppSetup.Start<UserAppModule>();
IUserService service = app.CreateComponent<IUserService>();
// you can use service now;
}

除了直接启动 UserAppModule ,

也可以将 UserAppModule 加到别的 应用模块 上,这个 自动注册 同样有效。

基于这样的方式, UserAppModule 完全可以开箱即用。

也可以说,任何一个 应用模块 都可以开箱即用。

自动映射配置文件

配置文件总是存在的, .NetFramework 提供了一整套 Configuration 方案。

但是这个方案略显笨重,并且在配置过程完全没有提示,必须依赖大量的文档说明。

很明显 JSONXML 轻巧很多,

并且还有 JsonSchema 可以向 IDE 提供提示功能。

所以 Reface.AppStarter 选择了 JSON 作为配置文件的格式 ,

Reface.AppStarter 会读取一个包含了所有配置的 JSON 文件,

将配置中的值映射的 对应 的类型和属性中,

并将这些 类型 以单例的模式注册到 IOC 容器中。


UserAppModule 为例,我们为其开发一个 配置类

配置类 就是 Reface.AppStarter 在解析 JSON 文件后反序列化的目标类型。

为一个类型添加 ConfigAttribute 就会通知 Reface.AppStarter : "嘿,这有一个配置类"

下面的例子表示,我们可以通过配置文件指定 根用户的信息

// Configs/UserConfig
[Config("User")]
public class UserConfig
{
public string RootUserName { get; set; } = "admin";
public string RootUserPassword { get; set; } = "888888";
}

ConfigAttribute 的构造函数中指定的 User 表示从配置文件的 User 节点中读取配置,配置文件大至如下:

{
"User" : {
"RootUserName" : "ROOT",
"RootUserPassword" : "123456789"
}
}

如果没有配置文件,或者配置文件中没有编写 User 则会使用属性中指定的默认值将配置类实例注册到 IOC 容器 中。

通过构造函数注入配置类就可以使用它们。

// Services/DefaultUserService.cs
public class DefaultUserService : IUserService
{
private readonly IRepository<User> userRepo;
private readonly UserConfig userConfig; public DefaultUserService(IRepository<User> userRepo, UserConfig userConfig)
{
this.userRepo = userRepo;
this.userConfig = userConfig;
} public void CreateUser(User user)
{
if(user.Name == userConfig.RootUserName)
throw new SomeException("无效的用户名,不能创建该用户");
this.userRepo.Insert(user);
}
}

ComponentScanAppModule 一样,需要为 UserAppModule 添加一样依赖来开启 自动映射配置文件 的功能。

// UserAppModule.cs
[ComponentScanAppModule] // 开启自动扫描注册组件
[AutoConfigAppModule] // 开启自动映射配置文件
public class UserAppModule : AppModule
{ }

现在启动 UserAppModule

或者将 UserAppModule 添加到其它 应用模块 的依赖项中,

这些功能都会被启用。

AppSetup.Start() 默认读取 app.json 文件,

若配置文件不存在,则直接使用 配置类 的默认属性值。

关于配置智能提示

通过 AppSetup.Start<T>() 运行程序后,会在输出目录下产生一个 app.schema.json 文件。

为你的 app.json 添加 $schema 属性,值指向 app.schema.json 文件,就可以得到智能提示功能:

你也可以通过上图显示的属性关闭生成 app.schema.json 的功能。


相关链接

Reface.AppStarter 基本示例的更多相关文章

  1. Reface.AppStarter 框架初探

    Reface.AppStarter 是一种基于 .NetFramework 的应用程序启动模式,使用该启动模式,你可以轻松的得到以下功能 : IOC / DI 自动注册与装配 简化配置 垂直模块化你的 ...

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

    类型扫描 是 Reface.AppStarter 提供的最基本.最核心的功能. AutoConfig , ComponentScan 等功能都是基于该功能完成的. 每一个使用 Reface.AppSt ...

  3. 事件总线功能库,Reface.EventBus 详细使用教程

    Reface.AppStarter 中的事件总线功能是通过 Reface.EventBus 提供的. 参考文章 : Reface.AppStarter 框架初探 使用 Reface.EventBus ...

  4. Reface.NPI 方法名称解析规则详解

    在上次的文章中简单介绍了 Reface.NPI 中的功能. 本期,将对这方法名称解析规则进行详细的解释和说明, 以便开发者可以完整的使用 Reface.NPI 中的各种功能. 基本规则 方法名称以 I ...

  5. EF 太重,MyBatis 太轻,ORM 框架到底怎么选 ?

    以 EF 为代表的基于 Linq 的 ORM 框架总是 很重. 他们的功能早已超出了一个 ORM 的范畴, ORM 是 Object Relational Mapping ,从名字上看,其初衷是将 数 ...

  6. 如何将 .NetFramework WebApi 按业务拆分成多个模块

    在 .NetFramework 中使用 WebApi ,在不讨论 微服务 的模式下,大部分都是以层来拆分库的 : 基础设施 数据存储层 服务层 WeApi 层 一些其它的功能库 项目结构可能会像下面这 ...

  7. 代理模式是什么?如何在 C# 中实现代理模式

    代理模式 并不是日常开发工作中常常用到的一种设计模式,也是一种不易被理解的一种设计模式.但是它会广泛的应用在系统框架.业务框架中. 定义 它的 定义 就如其它同大部分 设计模式 的定义类似,即不通俗也 ...

  8. Swift3.0服务端开发(一) 完整示例概述及Perfect环境搭建与配置(服务端+iOS端)

    本篇博客算是一个开头,接下来会持续更新使用Swift3.0开发服务端相关的博客.当然,我们使用目前使用Swift开发服务端较为成熟的框架Perfect来实现.Perfect框架是加拿大一个创业团队开发 ...

  9. .NET跨平台之旅:将示例站点升级至 ASP.NET Core 1.1

    微软今天在 Connect(); // 2016 上发布了 .NET Core 1.1 ,ASP.NET Core 1.1 以及 Entity Framework Core 1.1.紧跟这次发布,我们 ...

随机推荐

  1. go 项目目录结构

    网上有很多误人子弟的教程,   说项目下必须要有src,   傻逼玩意. 正确的路径应该是这样的: 所有go项目路径 src 项目1 项目2 项目N pkg bin 不是所有项目下必须建src, pk ...

  2. CSS中 隐藏元素的常用方法

    在CSS中,使某个元素在页面中消失的方法有很多,今天为大家介绍几种我常用的方式 . 一.display:none;   让元素直接从页面消失,不占用尺寸,会改变页面布局. 代码演示: 页面演示:在页面 ...

  3. opencv C++ Mat构造函数

    cv::Scalar scalar1(v); cv::Mat mat3(size,CV_8UC1,scalar1); std::cout<<mat3<<std::endl; s ...

  4. 我们是如何做go语言系统测试覆盖率收集的?

    工程效能领域,测试覆盖率度量总是绕不开的话题,我们也不例外.在七牛云,我们主要使用go语言构建云服务,在考虑系统测试覆盖率时,最早也是通过围绕原生go test -c -cover的能力来构建.这个方 ...

  5. 深入理解Java闭包概念

    闭包又称词法闭包 闭包最早定义为一种包含<环境成分>和<控制成分>的实体. 解释一:闭包是引用了自由变量的函数,这个被引用的变量将和这个函数一同存在. 解释二:闭包是函数和相关 ...

  6. 【题解】[USACO17JAN]Balanced Photo G

    题目链接:https://www.luogu.com.cn/problem/P3608 方法一 用树状数组求逆序对先后扫两遍,一次从前往后,一次从后往前,算出每头奶牛左右两边比她高的数量. 最后统计一 ...

  7. eclipse在debug模式下鼠标移动到变量上不显示值的问题

    在eclipse中调试时,鼠标移动到变量上不显示值,使用ctrl+shift+i,或者通过配置达到目的: Window->Preferences->Java->Editor-> ...

  8. javamelody对Java Application进行监控

    前面写过对于webapp,用javamelody来监控.分析性能是挺方便的:那要对普通的java应用进行监控,只需要在应用上启动一个嵌入式web容器就可以了. javamelody里面的war包就用了 ...

  9. 【博弈】HDU - 5963 朋友

    题目 B君在围观一群男生和一群女生玩游戏,具体来说游戏是这样的: 给出一棵n个节点的树,这棵树的每条边有一个权值,这个权值只可能是0或1. 在一局游戏开始时,会确定一个节点作为根.接下来从女生开始,双 ...

  10. JavaScript基础函数的声明(014)

    1.函数的重要地位 函数(functions)在JavaScript里有着重要的地位,其原因有二: 它们是一种特殊的对象 它们提供作用域 说函数在JavaScript里是特殊的对象,因为: 程序的执行 ...