配置功能增强

  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

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

最新文章

  1. [从产品角度学excel 04]-单元格的“衣服”
  2. shell命令lsof
  3. Java之重载与覆盖
  4. 【PowerOJ1740&amp;网络流24题 圆桌聚餐】(最大流)
  5. Myeclipse 2015 stable 1.0 完美破解方法(转自 http://yangl.net/2015/07/14/myeclipse_2015stable_1/)
  6. 在C#中使用LOG4NET(winform程序
  7. thinkphp模板调用函数用法
  8. 【转】一个域名对应多个IP地址,接下来系统是依据什么决定使用哪个IP地址的?
  9. 【BZOJ3262】 陌上花开
  10. 一本QT书,连接MySQL图文并茂
  11. JavaScript经典代码总结
  12. python3.5之输出HTML实体字符
  13. 利用宏定义令iOS项目当中的NSLog不执行
  14. jQuery中的index方法介绍
  15. SQL注入-攻入Apple ID钓鱼网站实录
  16. 最接近原生APP体验的高性能前端框架-MUI
  17. JavaOOP-集合框架
  18. 【转】JavaScript 错误处理与调试——“错误处理”的注意要点
  19. WEB框架-Django框架学习(二)- 模型层
  20. idea整合SVN以及SVN的使用

热门文章

  1. CC2530定时器模模式最大值计算
  2. Javascript中this作用域以及bind方法的重写
  3. Spring框架中获取连接池常用的四种方式
  4. 枚举 switch case 标签必须为枚举常量的非限定名称
  5. 云计算管理平台之OpenStack认证服务Keystone
  6. UI自动化测试不稳定的因素
  7. IC晶圆缺货涨价浪潮持续上涨 无线路由芯片WiFi模块受波及严重
  8. pycharm pro2020版专业版永久激活
  9. Docker(6)- docker info 命令详解
  10. c#分割