惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
Y
Y Combinator Blog
博客园 - 聂微东
GbyAI
GbyAI
H
Hackread – Cybersecurity News, Data Breaches, AI and More
月光博客
月光博客
T
The Blog of Author Tim Ferriss
爱范儿
爱范儿
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
雷峰网
雷峰网
MyScale Blog
MyScale Blog
Cloudbric
Cloudbric
T
Threatpost
Scott Helme
Scott Helme
N
News | PayPal Newsroom
Google DeepMind News
Google DeepMind News
Martin Fowler
Martin Fowler
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Engineering at Meta
Engineering at Meta
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
V2EX - 技术
V2EX - 技术
罗磊的独立博客
Attack and Defense Labs
Attack and Defense Labs
IT之家
IT之家
S
Secure Thoughts
C
Cyber Attacks, Cyber Crime and Cyber Security
D
Docker
V
V2EX
T
Troy Hunt's Blog
Forbes - Security
Forbes - Security
aimingoo的专栏
aimingoo的专栏
J
Java Code Geeks
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
T
Tor Project blog
P
Proofpoint News Feed
Recorded Future
Recorded Future
B
Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Simon Willison's Weblog
Simon Willison's Weblog
A
About on SuperTechFans
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
O
OpenAI News
V
Vulnerabilities – Threatpost
P
Privacy International News Feed
云风的 BLOG
云风的 BLOG

博客园 - 信息化建设

SAP 开发环境搭建入门 老程序员转测试 配置测试环境设置共享文件 提高团队效率 Visual Studio 2017十五项新功能体验 管理软件公司与互联网公司的区别 ERP程序开发中遇到的六种错误 ERP软件的价格设计 设计C/S架构应用程序的并发功能 C/S架构应用程序开发培训笔记 Enterprise Solution 进销存管理软件 C/S架构,支持64位系统 物流,资金流,信息流全面集成 Enterprise Solution 企业资源计划管理软件 C/S架构,支持64位系统,企业全面应用集成,制造业信息化 Enterprise Solution 3.1 企业应用开发框架 .NET ERP/CRM/MIS 开发框架,C/S架构,SQL Server + ORM(LLBL Gen Pro) + Infragistics WinForms .NET 程序集单元测试工具 SmokeTest 应用指南 LLBL Gen Pro 5.0 企业应用开发入门 解析大型.NET ERP系统 电子邮件系统帐户集成 财务模块功能中的凭证预览功能 工作流系统中的语法标记系统 通用附件管理功能改善 规范数据库表字段大小写 小写字段名全部更改为大写 Enterprise Solution 虚拟测试环境
CRM/ERP 企业管理软件中常见的七种程序设计模式
信息化建设 · 2016-05-28 · via 博客园 - 信息化建设

管理软件中的常见代码设计模式,来自于业务上的需要,有不恰当的地方欢迎批评指正。

1  RE-TRY 重试模式

场景:在连接数据库服务器时,如果SQL Server数据库没有启动或正在启动,我们需要有一个连接重试的策略。发送邮件通知时,我们也需要在发送失败后,多次的尝试发送以保证邮件能到达目的用户。

代码参考:

int maxRetry = 30;
int retryInterval = 10000;
for (int i = 1; i <= maxRetry; i++)
{
     try
     {
         //connect to the database server
      }
     catch (Exceptions exception)
     {
         if (i < maxRetry)
              Thread.Sleep(retryInterval);
         else 
             return; //返回或停止重试操作
     }
}

这种模式的主要是有一个重试行为,直到执行完成或是超过了约定的时间或次数则放弃。

2 Before-Perform-After  检查-执行-传送模式

场景:在做一项业务操作前,子类为了重写(override)基类的行为必须先做条件或环境检查,然后执行相应的业务操作,之后还可以将此次操作的结果继续传送到其它业务单元中。

以打印报表为例子,业务窗体需要先检查当前登录用户是否具备打印权限,如无权限则取消本次操作。代码例子:

protected internal virtual bool DoPerformPrint()
{
CancelableRecordPrintEventArgs e = new CancelableRecordPrintEventArgs(this.CurrentEntity, selectionForumlas, formulaFields, parameterFields);
this.OnBeforePrint(e);
if (this._beforePrint != null)
   this._beforePrint(this, e);
if (e.Cancel)
   return false;
this.Print(ref selectionForumlas, ref formulaFields, ref parameterFields);
EventArgs args = new EventArgs();
this.OnAfterPrint(args);
if (this._afterPrint != null)
   this._afterPrint(this, args);
}

通过这段代码应该容易理解我说的这种设计模式,第一段是条件检查,如果我们在子类中传入参数e.Cancel=true,则

此方法返回,不再执行Print方法。这种设计模式在事件机制中用的比较多。基类为了控制好业务单元整体的行为,同时又不失去灵活性,可以参照这种模式。

3 TRY  尝试模式

场景:尝试性的去执行某一个业务单元,并以它的结果来决定下一步的行为。

比如我们加载数据,如果加载失败了,则下一步要阻止数据绑定,再比如传送文件,如果双方连接失败,则要阻止文件传送。

代码例子:

private void LoadData()
{
  try
  {
     FindAndLoadData ()
     this._isDataLoaded = true;
  }
  catch (Exception exception)
  {
     this._isDataLoaded =false;
  }
}

这个模式的关键就在于定义的变量_isDataLoaded。在这个方法中,我们尝试去加载数据,如果有异常,则将此变量设为false表示加载失败,其它的业务单元检查到此变量的值以决定下一步操作。

这种行为也有危害,它隐藏了真实的异常原因,常常用在一些界面操作中,频繁的抛出各种异常会让用户反感,这时可以参考这种模式,尝试操作失败后,根据状态值阻止错误进一步发生。

4 BackgroundWorker 多线程工作

场景:在一些业务计算或是读取数据等耗费时间的业务操作,比如物需求计算,工作单批次发套料,计划订单发放等业务中,耗费的时间相当多,这种模式可改善效率。

应用代码例子:

List<LoadMasterScheduleWorker> workers = new List<LoadMasterScheduleWorker>();
for (int i = 0; i < MAX_RUNNING_THREAD; i++)
{
   LoadMasterScheduleWorker worker = new LoadMasterScheduleWorker(this, mrp, mpsRows, loadedMpsItems);
   workers.Add(worker);
}
WorkerThreadBase.StartAndWaitAll(workers.ToArray());
MAX_RUNNING_THREAD通常取当前系统的CPU个数(MAX_RUNNING_THREAD),在实际工作的线程中,采用下面的方法执行数据分配并启动相应的业务操作:
while (_mpsRows.Count > 0)
{
    DataRow mpsRow = null;
    if (!_mpsRows.TryTake(out mpsRow))
           break;
    LoadMasterSchedule(_mrp, mpsRow);
}

从Dictionary<DataRow>集合中不停的TryTake数据行(DataRow)来操作,直到取完所有的数据为止。

需要注意前一段方法的最后一句StartAndWaitAll方法,这里要等待当前所有子线程执行完成,避免数据没有操作完就进行后面的业务操作。

WorkerThreadBase的源代码参考这里。

http://stackoverflow.com/questions/597590/c-sharp-threading-patterns-is-this-a-good-idea

https://files.cnblogs.com/files/JamesLi2015/WorkerThreadBase.zip

5 Data Class 数据类

场景:减少代码中不必要的字符书写错误,改善程序可维护性。

旧代码是这样的:

DataTable query = new FastSerializableDataTable("BomRoutingTable");
query.Columns.Add("BomNo", typeof(string));
query.Columns.Add("SeqNo", typeof(decimal));
query.Columns.Add("OpCode", typeof(string));

应用数据类之后,代码是这样的:

DataTable query = new FastSerializableDataTable("BomRoutingTable");
query.Columns.Add(BomFields.BomNo, typeof(string));
query.Columns.Add(BomFields.SeqNo, typeof(decimal));
query.Columns.Add(BomFields.OpCode, typeof(string));
class BomFields
{
   public const string BomNo ="BomNo";
   public const string SeqNo  ="SeqNo";
   public const string OpCode ="OpCode";
}

新代码设计方式增加了一个类型定义,增加了一点复杂性,同时改善可维护性。创建DataTable的列来自于一个类型定义,如果有多个创建表的地方,则可显著改善书写效率。

6 History 历史记录

场景:ERP中标准物料清单需要通过ECN来变更,若是将此模式应用到其它业务,则做采购单修改或销售单修改等其它单据,也需要记录变更前的资料。

例子代码:

BomHistoryEntity bomHistory = new BomHistoryEntity();
InitialBomHistoryHeaderInActivation(bomHistory, ecn, canUpdate);
return bomHistory;

变更记录相当于流水帐,记录每次业务修改前的值。变更记录还可以通过数据审计功能来实现,不过审计功能过于抽象,只能记录到表的字段变化,要实现两行数据记录字段值的精确比较,还需要开发接口程序以适应特定的业务单据。

历史记录模式最大的杀手就是反审核,反过帐,若业务单据可以反复修改,审核之后退回,反审核又可以修改,这样反复的操作,会产生大量的历史数据。

7 Hash Verify 哈希值验证

场景:文件传送时,在文件传送完成后,需要验证文件传送前后的MD5值或SHA1是否一致以保证文件没有丢失字节。

在一些重要的业务场景,比如转帐,充值,系统登录,在业务操作完成后我们还需要验证一下业务发生是否合理。

代码例子:

session = Login(userId, passwordHash, companyCode, languageCode, hostEntry.HostName, domainUserName, currentProcess.SessionId)
string clientHash = Shared.GetMD5HashValue(string.Concat(new object[] { 
                    AssemblyVersion.Company, AssemblyVersion.Product, AssemblyVersion.Version, AssemblyVersion.FileVersion, AssemblyVersion.Product, session.UserId, session.UserGroup, session.CompanyCode, session.SessionId, session.LanguageCode, session.HostName, session.DomainUserName}));
              
string privateKey = "...";
string serverHash = RSACryptionHelper.DecryptString(session.ClientHashValue, privateKey);
if (string.CompareOrdinal(clientHash, serverHash) != 0)
   throw new AppException("Unable to login, unexpected error detected");
 

第一行代码登录成功之后,服务器返回一个session会话对象,此对象包含一个ClientHashValue的MD5值,与此同时我们将客户端的登入信息,再次构造成一个字符串进行MD5处理,以比较两者的值是否一致,若不一致可能是非法登录请求,抛出异常,阻止登入系统。

这个方法也可应用于多版本策略中,系统要阻止3.2版本的客户端登录到2.1版本的服务器端,注意到上面的代码中有Version参数,会抛出异常。这样可保证一台电脑安装多个版本的服务器端而不相互冲突。