配置功能增强

  Abp定义了各种配置接口,但是没有定义这些配置数据从哪里来,但是管理配置数据对于一个应用程序来说,是必不可少的一件事情。

  .net的配置数据管理,一般放在Web.config文件或者App.config文件里面,.net core也是定义在一个json的配置文件里面。我们也可以自定义configSection,但是对于稍微大型一点的应用,配置可能非常的复杂,代码比较规范的,对于每一个配置或者配置组都写好注释,但是毕竟是比较麻烦的一件事情。

  我们可以对所有的配置进行集中的管理,把所有的配置放到一个固定的应用程序目录下面,每一个功能模块定义一个配置文件,配置文件名称做为分组名称,比如我们可以放到应用的Configuration/AppSettings目录下,配置文件格式如下:

  那么我们可以在应用启动的时候,读取此目录下的所有文件,放到全局静态的字典里面,那么我们调用的时候,只需要调用静态方法,传递分组和key即可,比如:AppSettingManager.GetSetting("WorkflowNotice","SignalrTaskTitle"),代码比较简单,实现如下:

public static class AppSettingManager
{
#region Private Memeber private static Dictionary<string, Dictionary<string, string>> dictAppsettings;
static AppSettingManager()
{
dictAppsettings = new Dictionary<string, Dictionary<string, string>>();
} private static string BaseFolderPath
{
get
{
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configuration/AppSettings");
}
} private static Dictionary<string, string> LoadSettings(string filePath)
{
if (!File.Exists(filePath))
{
return new Dictionary<string, string>(0);
}
Dictionary<string, string> rst = new Dictionary<string, string>();
XElement doc = XElement.Load(filePath);
foreach (var x in doc.Descendants("add"))
{
if (x.Attribute("key") == null || x.Attribute("key").Value == null || x.Attribute("key").Value.Trim().Length <= 0)
{
throw new ApplicationException("配置文件格式有错误,存在add节点,但是没有key属性,路径为: " + filePath + ", 请检查!");
}
string key = x.Attribute("key").Value.Trim().ToUpper();
if (rst.ContainsKey(key))
{
throw new ApplicationException($"配置文件存在相同的Key[{x.Attribute("key").Value.Trim()}],路径为:{filePath},请检查!");
}
string value = x.Attribute("value") == null ? null : (x.Attribute("value").Value == null ? null : x.Attribute("value").Value.Trim());
value = value ?? x.Value;
rst.Add(key, value);
}
return rst;
} #endregion public static void InitAppsettings()
{
var strFileInfos = Directory.GetFiles(BaseFolderPath, "*.config");
foreach(var strFileInfo in strFileInfos)
{
var fileName = Path.GetFileNameWithoutExtension(strFileInfo);
var fileNameAppsetting = LoadSettings(strFileInfo);
dictAppsettings.Add(fileName, fileNameAppsetting);
}
} public static string GetSetting(string fileTitle, string key)
{
if(!dictAppsettings.ContainsKey(fileTitle))
{
return "";
}
key = key.ToUpper(); if(!dictAppsettings[fileTitle].ContainsKey(key))
{
return "";
} return dictAppsettings[fileTitle][key];
}
}

  还有另外一种情况,我们定义了配置接口,比如abp定义了各种配置接口,但是配置数据也需要从其他地方读取出来,常见的方式也是配置文件,这种情况,我们可以定义Xml与配置实现的映射,启动的时候读取每一个文件映射到接口配置实现类。

配置帮助类:

public class XmlConfigProvider
{
public static T GetConfig<T>(string fileName, string relativePath = "")
{
if(string.IsNullOrEmpty(relativePath))
{
relativePath = @"Configuration\XmlConfig";
} string fileFullName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, relativePath, fileName);
if(!File.Exists(fileFullName))
{
return default(T);
}
return LoadFromXml<T>(fileFullName);
} private static T LoadFromXml<T>(string filePath)
{
FileStream fs = null;
try
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
return (T)serializer.Deserialize(fs);
}
finally
{
if (fs != null)
{
fs.Close();
fs.Dispose();
}
}
}
}

配置实现类举例:

[Serializable]
[XmlRoot]
public class SocketServiceConfiguration : ISocketServiceConfiguration
{
[XmlAttribute]
public int Backlog { get; set; }
/// <summary>
/// 客户端解码类型
/// </summary>
[XmlAttribute]
public EMessageCode MessageCode { get; set; }
/// <summary>
/// 端口
/// </summary>
[XmlAttribute]
public int Port { get; set; } [XmlAttribute]
public bool Libuv { get; set; } public SocketServiceConfiguration()
{
Backlog = 100;
Libuv = false;
MessageCode = EMessageCode.MessagePack;
}
}

使用,可以在Module里面读取

// 注册客户端配置,固定从Xml文件读取
SocketServiceConfiguration socketServiceConfiguration = XmlConfigProvider.GetConfig<SocketServiceConfiguration>("SocketServiceConfiguration.xml");
IocManager.IocContainer.Register(
Component
.For<ISocketServiceConfiguration>()
.Instance(socketServiceConfiguration)
);

T4模版,部分代码自动生成

  有很多方便的代码生成工具,好不好用我就不说了,只要能够提高我们平时的编码效率,减少一些繁琐的基础编码工作,对我们有帮助,总归是好的。

  T4模版,直接在VS里面,编辑之后保存,就会根据模版生成对应的代码文件,我们可以自定义代码生成规则,完全的自定义,可见即可得。

  我这里的配置,是围绕着一个Xml文件,内容是对一个实体信息集合的描述,Xml文件格式如下:

<FrameworkTemplates>
<FrameworkTemplate Name="WF_FlowType" Type="DomainEntity" DataTableName="WF_FlowType" Inherit="AuditedEntity&lt;Guid&gt;, IMayHaveTenant">
<TemplateItem>
<Field>Id</Field>
<CnName>主键</CnName>
<Type>guid</Type>
<IsRequred>false</IsRequred>
</TemplateItem>
<TemplateItem>
<Field>TypeName</Field>
<CnName>类型名称</CnName>
<Type>string</Type>
<IsRequred>true</IsRequred>
<MaxLength>50</MaxLength>
</TemplateItem>
<TemplateItem>
<Field>ParentId</Field>
<CnName>父节点Id</CnName>
<Type>guid</Type>
<IsRequred>false</IsRequred>
</TemplateItem>
<TemplateItem>
<Field>Notes</Field>
<CnName>备注</CnName>
<Type>string</Type>
<IsRequred>false</IsRequred>
<MaxLength>500</MaxLength>
</TemplateItem>
</FrameworkTemplate>
……
</FrameworkTemplates>

  那个我们可以根据这个Xml文件的描述,生成DbContext、DomainEntity、CreateDto、UpdateDto、ApplicationService等公共的信息,每一个类都需要标识为partial,方便我们编写非公共的部分代码。生成DomainEntity代码举例:

<#@ template language="C#" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Xml"#>
<#@ import namespace="System.Xml" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ include file="$(SolutionDir)\Resources\T4\TemplateFilemanager.CS.ttinclude" #>
<#
var manager = TemplateFileManager.Create(this);
string strProjectName = "Workflow";
EnvDTE.DTE dte = (EnvDTE.DTE) ((IServiceProvider) this.Host).GetService(typeof(EnvDTE.DTE));
XmlDocument doc = new XmlDocument();
var strSolutionPath = System.IO.Path.GetDirectoryName(dte.Solution.FullName);
var strTemplateFilePath = System.IO.Path.Combine(strSolutionPath, @"Resources\Modules\Workflow.xml");
doc.Load(strTemplateFilePath);
var frameworkTemplateNodes = doc.SelectNodes("/FrameworkTemplates/FrameworkTemplate[@Type='DomainEntity']");
foreach (XmlNode templateNode in frameworkTemplateNodes)
{
manager.StartNewFile(templateNode.Attributes["Name"].Value + ".cs");
var inheritNode = templateNode.Attributes["Inherit"];
var strInherit = "";
#>
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text; using CK.Sprite.Domain.Entities;
using CK.Sprite.Domain.Entities.Auditing;
using CK.Workflow.DomainInterface; namespace CK.<#= strProjectName #>.DomainEntity
{
<# if(inheritNode==null || inheritNode.Value=="") // ---输出Class---
{
#>
public partial class <#= templateNode.Attributes["Name"].Value #>
<#
}
else
{
strInherit = templateNode.Attributes["Inherit"].Value;
#>
public partial class <#= templateNode.Attributes["Name"].Value #> : <#= strInherit #>
<#
} // ---End输出Class---
#>
{ <#
foreach (XmlNode itemNode in templateNode.ChildNodes)
{
string tempTypeName = "";
string tempFieldName = "";
string tempFieldCnName = "";
string tempIsRequred = "";
string tempMaxLength = "";
string strIsRequred = "";
string strMaxLength = "";
if (itemNode.LocalName == "TemplateItem")
{
tempFieldName = itemNode.ChildNodes[0].InnerText;
tempFieldCnName = itemNode.ChildNodes[1].InnerText;
tempTypeName = itemNode.ChildNodes[2].InnerText;
tempIsRequred = itemNode.ChildNodes[3].InnerText;
for(var i=0;i< itemNode.ChildNodes.Count;i++ )
{
if(itemNode.ChildNodes[i].Name == "MaxLength")
{
tempMaxLength = itemNode.ChildNodes[i].InnerText;
}
}
if(tempFieldName == "Id")
{
continue;
}
if(tempIsRequred.ToLower()=="true")
{
strIsRequred = "[Required]";
}
if (!string.IsNullOrEmpty(tempMaxLength) && tempMaxLength != "0")
{
strMaxLength = "[StringLength("+ tempMaxLength + ")]";
}
switch (tempTypeName)
{
case "int":
if (tempIsRequred.ToLower() == "false")
{
tempTypeName = "int?";
}
break;
case "double":
if (tempIsRequred.ToLower() == "false")
{
tempTypeName = "double?";
}
break;
case "date":
case "datetime":
if (tempIsRequred.ToLower() == "false")
{
tempTypeName = "DateTime?";
}
else
{
tempTypeName = "DateTime";
}
break;
case "guid":
if (tempIsRequred.ToLower() == "false")
{
tempTypeName = "Guid?";
}
else
{
tempTypeName = "Guid";
}
break;
}
#>
/// <summary>
/// <#= tempFieldCnName #>
/// </summary>
<#= strIsRequred #>
<#= strMaxLength #>
public <#= tempTypeName #> <#= tempFieldName #> { get; set; }
<#
}
}
if (!string.IsNullOrEmpty(strInherit))
{
List<string> autoFields = new List<string>();
var inheritList = strInherit.Replace(" ","").Split(new char[] { ',' });
foreach (var inherit in inheritList)
{
switch (inherit)
{
case "IMayHaveTenant":
autoFields.Add("public int? TenantId { get; set; }");
break;
case "IMustHaveTenant":
autoFields.Add("public int TenantId { get; set; }");
break;
case "IPassivable":
autoFields.Add("public bool IsActive { get; set; }");
break;
case "IHasCreationTime":
autoFields.Add("public DateTime CreationTime { get; set; }");
break;
case "ICreationAudited":
autoFields.Add("public DateTime CreationTime { get; set; }");
autoFields.Add("public long? CreatorUserId { get; set; }");
break;
case "IHasModificationTime":
autoFields.Add("public DateTime? LastModificationTime { get; set; }");
break;
case "IModificationAudited":
autoFields.Add("public DateTime? LastModificationTime { get; set; }");
autoFields.Add("public long? LastModifierUserId { get; set; }");
break;
case "ISoftDelete":
autoFields.Add("public bool IsDeleted { get; set; }");
break;
case "IHasDeletionTime":
autoFields.Add("public DateTime? DeletionTime { get; set; }");
autoFields.Add("public DateTime CreationTime { get; set; }");
break;
case "IDeletionAudited":
autoFields.Add("public DateTime? DeletionTime { get; set; }");
autoFields.Add("public long? DeleterUserId { get; set; }");
autoFields.Add("public DateTime CreationTime { get; set; }");
break;
case "IAudited":
autoFields.Add("public DateTime? LastModificationTime { get; set; }");
autoFields.Add("public long? LastModifierUserId { get; set; }");
autoFields.Add("public DateTime CreationTime { get; set; }");
autoFields.Add("public long? CreatorUserId { get; set; }");
break;
case "IFullAudited":
autoFields.Add("public bool IsDeleted { get; set; }");
autoFields.Add("public long? DeleterUserId { get; set; }");
autoFields.Add("public DateTime? DeletionTime { get; set; }");
autoFields.Add("public DateTime? LastModificationTime { get; set; }");
autoFields.Add("public long? LastModifierUserId { get; set; }");
autoFields.Add("public DateTime CreationTime { get; set; }");
autoFields.Add("public long? CreatorUserId { get; set; }");
break;
}
}
autoFields = autoFields.Distinct().ToList();
foreach(var autoField in autoFields)
{
#> <#= autoField #>
<#
}
}
#> }
}
<#
}
manager.Process();
#>

  另外,我们还可以定义前端的List和Form页面,甚至不用编写前端代码都可以。

  对于Abp的改造还有其他一些地方,比如Signalr的消息通知、Application是否登录权限验证等,相对来说比较简单,就不描述了。

  前面两个大的章节的内容就告一段落了,这些是做为整体工作流引擎的框架依赖,所以写在前面,这部分内容是脱离工作流独立存在的,现在在构思工作流部分的内容,工作流部分可能需要对WWF有所了解的朋友才更加容易理解,但是做为研发流程引擎的朋友应该也可以提供一些思路,问题可以直接留言或者QQ联系我:523477776

  到目前为止还没有人进行过评论,也比较影响写文章的激情,不知道是否写得有问题,如果有问题对其他人造成错误的引导,那就提前说一声抱歉了。

企业级工作流解决方案(十五)--集成Abp和ng-alain--Abp其他改造的更多相关文章

  1. 企业级工作流解决方案(十四)--集成Abp和ng-alain--自动化脚本

    对于.net方向,做过自动化的,应该没有人不熟悉msbuild吧,非常强大的代码编译工具,.net平台的编译工作都是交给他来完成的,包括.net core的命令,本质上都是调用msbuild来执行的 ...

  2. 企业级工作流解决方案(十)--集成Abp和ng-alain--权限系统

    权限系统 应用系统离不开权限控制,权限中心不一定能抽象出所有的业务场景,这里定义的权限系统不一定能够满足所有的场景,但应该可以满足多数的业务需求. Abp的zero项目也定义了权限相关的表,但里面很多 ...

  3. 企业级工作流解决方案(十二)--集成Abp和ng-alain--用户身份认证与权限验证

    多租户 如果系统需要支持多租户,那么最好事先定义好多租户的存储部署方式,Abp提供了几种方式,根据需要选择,每一个用户身份认证与权限验证都需要完全的隔离 这里设计的权限数据全部存储在缓存中,每个租户单 ...

  4. 企业级工作流解决方案(十三)--集成Abp和ng-alain--数据库读写分离

    说到程序里面数据库管理,无非就是两件事情,一是数据库操作,对于数据库的操作,各种程序语言都有封装,也就是所谓的ORM框架,.net 方向一般用得比较多和就是.net framework和dapper, ...

  5. 企业级工作流解决方案(十一)--集成Abp和ng-alain--权限系统服务

    权限系统主要定义为管理员增删改查权限数据,直接读取数据库,权限系统服务主要定义为供其他系统调用的权限验证接口,定义为两个不同的微服务. 权限系统有一个特点,数据变动比较小,数据量本身并不是很大,访问量 ...

  6. 企业级工作流解决方案(六)--微服务消息处理模型之与Abp集成

    身份认证传递 对于Abp比较熟悉的朋友应该对他里面的用户身份认证比较熟悉,他是通过实现微软提供的权限认证方式实现的,用户登录身份信息存储在System.Security.Claims.ClaimsPr ...

  7. SpringCloud微服务实战——搭建企业级开发框架(十五):集成Sentinel高可用流量管理框架【熔断降级】

      Sentinel除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一.由于调用关系的复杂性,如果调用链路中的某个资源不稳定,最终会导致请求发生堆积.Sentinel ...

  8. SpringBoot入门教程(十五)集成Druid

    Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0.DBCP.PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB ...

  9. 企业级工作流解决方案(八)--微服务Tcp消息传输模型之服务端处理

    服务端启动 服务端启动主要做几件事情,1. 从配置文件读取服务配置(主要是服务监听端口和编解码配置),2. 注册编解码器工厂,3. 启动dotnetty监听端口,4. 读取配置文件,解析全局消息处理模 ...

随机推荐

  1. python web自动化上传文件工具

    工具下载地址:链接:https://pan.baidu.com/s/1cHdNHW 密码:56bp说明:1.WinSpy-1.0.2.7z解压即可.2.pywin32的exe程序,根据自己安装的pyt ...

  2. centos7安装ifconfig

    1,找出哪个包提供了ifconfig命令 yum provides ifconfig 2,通过输出,发现是net-tools提供了ifconfig命令 yum install net-tools

  3. mock.js 学习

    安装 npm install mockjs 使用 // 引入 import Mock from 'mockjs' Mock.setup({ timeout: '200 - 400' }) const ...

  4. PHP代码审计04之strpos函数使用不当

    前言 根据红日安全写的文章,学习PHP代码审计的第四节内容,题目均来自PHP SECURITY CALENDAR 2017,讲完题目会用一个实例来加深巩固,这是之前写的,有兴趣可以去看看: PHP代码 ...

  5. 使用 tabindex 配合 focus-within 巧妙实现父选择器

    本文将介绍一个不太实用的小技巧,使用 tabindex 配合 :focus-within 巧妙实现父选择器. CSS 中是否存在父选择器? 这是一个非常经典的问题,到目前为止,CSS 没有真正意义上被 ...

  6. 4G DTU是什么 可以应用于哪些行业?

    4G是什么? 4G是移动电话网络通过蜂窝塔传输的信号的名称,蜂窝塔连接到更宽的互联网.这些是当今智能手机使用的信号,当您外出时,可以通过手机上网,因此他们不依赖电缆或光纤,也就是说无线网. 使用合适的 ...

  7. Java学习的第四十五

    1.例5.7求三个字符串中的最大值 import java.util.Scanner; public class Cjava { public static void main(String[]arg ...

  8. python中的递归

    python中的递归 关注公众号"轻松学编程"了解更多. 文章更改后地址:传送门 间接或直接调用自身的函数被称为递归函数. 间接: def func(): otherfunc() ...

  9. 这个蒟蒻也开通wordpress博客啦~

    RT,欢迎各位dalao常来菜鸡博客玩,加友链什么的最好了~ 传送门(请注意https不能漏,因为本菜鸡的家用宽带只有443端口). 以后博客园的博客会同步更新. 这个人太颓了,只打算在wordpre ...

  10. AC 自动机刷题记录

    目录 简介 第一题 第二题 第三题 第四题 第五题 第六题 简介 这就是用来记录我对于<信息学奥赛一本通 · 提高篇>一书中的习题的刷题记录以及学习笔记. 一般分专题来写(全部写一起可能要 ...