zoukankan      html  css  js  c++  java
  • 企业级工作流解决方案(十五)--集成Abp和ng-alain--Abp其他改造

    配置功能增强

      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 = @"ConfigurationXmlConfig";
                }
    
    
                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)ResourcesT4TemplateFilemanager.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, @"ResourcesModulesWorkflow.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

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

  • 相关阅读:
    Netweaver和CloudFoundry是如何运行Web应用的?
    5分钟部署一个Hello World Servlet到CloudFoundry
    Netweaver和CloudFoundry的服务器日志
    ABAP和Java的单元测试Unit Test
    在浏览器里使用SAPGUI
    从程序猿到SAP产品经理,我是如何转型的?
    Java实现 LeetCode 521 最长特殊序列 Ⅰ(出题人:“就是喜欢看你们不敢相信那么简单,又不敢提交的样子。”)
    Java实现 LeetCode 520 检测大写字母
    Java实现 LeetCode 520 检测大写字母
    Java实现 LeetCode 520 检测大写字母
  • 原文地址:https://www.cnblogs.com/spritekuang/p/10831104.html
Copyright © 2011-2022 走看看