配置功能增强

  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. Linux基础命令之getent

    getent命令简述 getent - get entries(entry的复数,条目.项目.记载.记录) getent命令可以用来察看系统的数据库中的相关记录 经常使用getent查看用户账号: 之 ...

  2. Linux运维学习第一周记

    1 当年白岳伴清游, 2 江石台空一苇浮. 3 缥渺临风闻郢曲, 4 殷勤歧路看吴钩. 老气横秋方知世间沧桑! 以前一直忙,没有时间沉浸下来学习,一直都是浮着. 至此大疫,给生命按下了暂停键. 踏踏实 ...

  3. JS DIV列表自动滚动带停顿,滚动到底部后自动滚动到顶部

    setInterval -- 间隔执行函数:element.scrollTop -- 元素滚动条距头部的距离: 因为执行代码需要时间,所以最终动态时间会比设置的要慢 var slide = new S ...

  4. HTML轮播(3)

    前言 现在给轮播加上可视化的点,实际这样的轮播已经算完成的了 CSS #LB { width: 100%; height: 948px; overflow: hidden; position:rela ...

  5. kubernetes教程第一章-kubeadm高可用安装k8s集群

    目录 Kubeadm高可用安装k8s集群 kubeadm高可用安装1.18基本说明 k8s高可用架构解析 kubeadm基本环境配置 kubeadm基本组件安装 kubeadm集群初始化 高可用Mas ...

  6. 从一个例子入手Istio

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 本文使用的Istio源码是 release 1.5. 本篇是Istio系列的第一篇,希望 ...

  7. webpack 单独打包指定JS文件

    背景 最近接到一个需求,因为不确定打出的前端包所访问的后端IP,需要对项目中IP配置文件单独拿出来,方便运维部署的时候对IP做修改.因此,需要用webpack单独打包指定文件. CommonsChun ...

  8. SpringBoot第二集:注解与配置(2020最新最易懂)

    2020最新SpringBoot第二集:基础注解/基础配置(2020最新最易懂) 一.Eclipse安装SpringBoot插件 Eclipse实现SpringBoot开发,为便于项目的快速构建,需要 ...

  9. Docker知识进阶与容器编排技术

    目录 1 使用Dockerfile定制redis镜像 1.1 环境准备 1.2 编写Dockerfile文件 1.3 通过Dockerfile构建镜像 1.4 通过镜像运行容器 1.5 官方镜像替代我 ...

  10. 时间UTC格式装换php时间格式

    date_default_timezone_get("UTC");date("Y-m-d", strtotime("2017-11-13T18:04: ...