一直用.NET做ERP/MIS类型的软件,学业不精,可以完成工作任务,业余时间也制作一些集成化的工具包,下面分享一下我认为的.NET领域里的底层技术。参加工作已经有五年了。由于是非计算机科系,一开始的工作是做网页,维护ASP脚本程序,有时候也要做图片,总而言之,一项任务交到手里,没有借口说不会做,想办法弥补然后把任务做完。后来才得到机会,参与编程开发工作。而且,当时一心想用C#.NET做开发,不希望参与DELPHI的开发,偏偏工作经过层层的转发,交到手里的就是DELPHI开发。经历努力,再后来就真的用.NET进行开发工作,一直持续到现在。消极的观念会说,学的很杂,做的也杂,积极一点的想法,把老板交给你的任务完成,就是称职的员工。现在正值毕业生找工作的时间,一时找不到开发的工作,也不要急,先听从公司的安排做个软件测试,或是文档管理之类的职位,等有空缺的开发人员的机会,你再毛遂自荐,同样可以进入程序员这个领域。机会是留给有准备的人的,难道不是吗?
一直用.NET做ERP/MIS类型的软件,学业不精,可以完成工作任务,业余时间也制作一些集成化的工具包,下面分享一下我认为的.NET领域里的底层技术。
1通信技术(Remoting,WCF,ASMX)
通常的三层架构,数据库,数据访问层,界面层。通讯技术定义了三层组件之间的调用约定,以及方法。这样说起来有些抽象,举例为证。现在要制作一个进销存项目,基本的功能是进仓,出仓,转仓,查询功能要能查询到仓库进出日记帐,
库存余额。对于一笔物料为FLEX0901的进仓业务,用ORM的方式完成,伪代码如下
InventoryMovmentEntityreceipt=newInventoryMovmentEntity(“REF1108080001”,"RECEIPT”);
InventoryMovmentDetailEntitydetail=receipt.InventoryMovmentDetails.AddNew();
detail.ItemNo=”FLEX0901”;
detail.MoveDate=DateTime.Now;
InventoryMovementDAL.Instance.Save(receipt);
这里有几个问题处理的不错,比如
1)ORM的数据读写,关注面从拼凑SQL读写到构建实体,这是进步。如果用DAL+SQLScript的方式完成,伪码如下
stringMySQL=”INSERTICMOVH(RefNo,Direction)VALUES(‘REF1108080001’,'RECEIPT’)";
InventoryMovementDAL.Instance.ExecuteNonQuery(mySql);
mySql=”INSERTICMOVD(ItemNo,MoveDate)VALUES(‘FLEX0901’,'2011/8/8’)";
InventoryMovementDAL.Instance.ExecuteNonQuery(mySql);
这里省略了拼凑参数值的句段,直接把值放到SQL语句中去。
后一种方式,明显的缺陷时,当添加新的字段,对系统扩展时,修改和维护起来的成本,明显大于前者。
2)使用了单件模式,InventoryMovementDAL.Instance来统一操作数据读写,而不是这样
InventoryMovementDALinventoryDAL=newInventoryMovementDAL();
inventoryDAL.Save(receipt);
从这个单件模式,引出了通讯技术的必要性。当有很多个用户,同时操作进仓功能,意味着同时有很多个InventoryMovementDAL的数据写入操作,这时产生的问题
◆不好控制前后两张单据的进仓单参考编号重复。为保证不重复,在保存之前,我们需要到数据库检查一次是否已经存在该参考编号的进仓单,也就是InventoryMovementDAL的Save方法的开头,
要包含这样一段代码,以DAL+SQLScript的方式表示
tringmySql=”SELECTCOUNT(1)FROMICMOVHWHERERefNo=’REF1108080001’";
boolexisting=InventoryMovementDAL.Instance.ExecuteScalar(mySql)>0;
可以想像,当并发用户为100时,每一笔进仓业务,需要预先一次数据检查,来回于数据库,这样的程序性能肯定不好。
◆对于库存报表,多个并发的InventoryMovementDAL会导致数据前后不统一。库存余额报表现在可以读到物料FLEX0901还有100个pc,一会当有进仓单入库200pc的FLEX0901时,如果不手动刷新数据,此时的报表仍然显示的物料FLEX0901的库存余额是100,而不是300.这有时候是不可接受的结果。
可以做一个timer,定期刷新库存余额。这里的问题是,InventoryMovementDAL只有一个实例,无法自己告诉自己,已经有新的库存了,重新读取数据。这里需要一种并发机制,告诉InventoryMovementDAL实例,有新的数据加入到库存余额中,需要刷新报表。
◆有一些数据项,是全局的,对于整个系统都是唯一的,需要特殊处理。
比如ERP系统允许的并发用户,同时允许有10个用户连接进入系统,超过则无法处理;
ERP系统的一个用户把当前系统的默认货币从HK$改进了US$,其它的用户,要能知道这个改变,此时,不太可能让当前正在操作业务的用户退出,重新进入系统;
当发生网络故障时,ERP系统要能知道网络故障,并suspend当前正在进行的操作;这一点可以理解为一条有效的改善,当无法连接到SQLServer时,要阻止当前正在录入数据的用户,否则他很辛苦的录入了数据,而系统又无法保存,给出一个提示Anetwork-relatedorinstance-specificerroroccurredwhileestablishingaconnectiontoSQLServer.TheserverwasnotfoundorwasnotAccessible.VerifythattheinstancenameiscorrectandthatSQLServerisconfiguredtoallowremoteconnections用户会抱怨你的软件没有做好,尽管这不是你的错。
如下图,当前用户正在编辑客户资料,发生网络故障,界面被Disable了。借助于通讯技术中的并发,可以实现。
通篇都没有给Remoting,WCF打广告,说Remoting,WCF的好处。当你的程序遇到这几个问题时,是否可以考虑下通讯技术,来改善性能和客户体验。
2反射,动态编译
反射是动态获取程序集的元数据的一种技术,这句话是做.NET程序员面试题目的一个的答案,你可选择记住它,就好比高中生物学里面讲到的细胞的结构的课程时,细胞由细胞膜,细胞质和细胞核组成。根据做程序的经验,Neveraskwhy不是好习惯,即使是微软的API,有时候违反了调用约定,也会很抓狂。
请看下面这一段代码
Assemblyassembly=Assembly.GetExecutingAssembly();
objectentryForm=Activator.CreateInstance(formBaseType)asForm;
entryForm.MdiParent=this;
entryForm.Show();
entryForm.Activate();
代码的含义比较简单,从当前程序集中创建formBaseType类型,并调用它的方法。从方法名上来看,大概可以看出,这是一段MDI创建child子窗体,并显示子窗体的代码。
这段小代码,也是插件式框架的基本思路,请参考《ManagementConsole工具管理类软件通用开发框架(开放源码)》中的例子来体会它的用处。
在ERP/MIS系统中,应用反射的例子,实在是太多了。数据访问接口InventoryMovementDAL,借助于反射,来查找并调用它的实现类;窗体也它的子窗体借助于反射来获取属性,传递值;ERP系统的整个框架,也是借助于反射搭建起来.
看下面的图,ERP的三个模块Paradox.ERP.SystemAdministration,Paradox.ERP.Engineering,Paradox.ERP.Inventory被Paradox.Framework.Kernal反射调用,如果再写一个Paradox.ERP.Sales的销售模块,几乎不需要改动,就可以让它被框架调用。
动态编译的例子,是应用到一个工资系统中.请看图
对于如何解析工资的formular公式,有若干种办法,这里使有的是动态编译的方法。把每一个工资项看成是一个类型class的属性,Formular的内容则放到一个方法中去,当成表达式计算求职,最后应用反射,返回各属性的值即可。
publicclassFormularCalculation
{
publicstaticobjectBuild(string[]items,stringformular)
{
stringnameSpace="A";
stringclassName="FormularCalculation";
stringmethodName="Run";
CSharpCodeProvidercompiler=newCSharpCodeProvider();
CompilerParametersparas=newCompilerParameters();
paras.GenerateExecutable=false;
paras.GenerateInMemory=true;
StringBuilderclassSrc=newStringBuilder();
classSrc.Append("usingSystem;"+Environment.NewLine);
classSrc.Append("namespace"+nameSpace+"{"+Environment.NewLine);
classSrc.Append("publicclass"+className+"{"+Environment.NewLine);
foreach(stringiteminitems)
{
classSrc.Append("publicdecimal"+item+";"+Environment.NewLine);
}
classSrc.Append("publicvoidRun(){基本工资=5000;"+Environment.NewLine);
string[]format=Regex.Split(formular,Environment.NewLine);
foreach(stringpropinformat)
{
classSrc.Append(prop+";"+Environment.NewLine);
}
classSrc.Append("}"+Environment.NewLine);
classSrc.Append("}"+Environment.NewLine);
classSrc.Append("}"+Environment.NewLine);
stringsource=classSrc.ToString();
CompilerResultsresult=compiler.CompileAssemblyFromSource(paras,source);
CompilerErrorCollectionerror=result.Errors;
Assemblyassembly=result.CompiledAssembly;
objecteval=assembly.CreateInstance(nameSpace+"."+className);
MethodInfomethod=eval.GetType().GetMethod(methodName);
objectreobj=method.Invoke(eval,null);
returneval;
}
}
调用方法如下
string[]items={"应发合计","基本工资","奖金","福利费","扣款合计","社保","税","实发合计","应税所得额"};
stringformular=@”应发合计=基本工资+奖金+福利费-扣款合计;
扣款合计=社保+税+应税所得额;
实发合计=应发合计-扣款合计;";
objectobj=FormularCalculation.Build(items,formular);
Typetype=obj.GetType();
foreach(PropertyInfofiintype.GetProperties(BindingFlags.NonPublic|BindingFlags.Public
|BindingFlags.Instance|BindingFlags.DeclaredOnly))
{
stringvalue=fi.Name;//取到计算后的各个属性的值
}
动态编译还应用于软件的加密,在内存中产生加密程序的源代码,动态编译并运行,检测是否符合license授权。
3ORM(NHibernate,LLBLGen)对象关系映射
虽然可以找出很多理由来拒绝ORM,比如performance不好,接口不好用,没有NHibernate设计器,是的,这都是理由。但是,一旦接触过ORM之后,我发现做系统再也离不开这个工具。相对于ERP/MIS类的系统,大部分时间都是和SQL的读写在搏斗,ORM带给你的几个好处,是不可忽视的。
1)增加或删除数据库字段,界面和程序几乎不需要改动。这一点我非常认同,即使是很稳定的系统,也避免不了要做customization,也要加些字段,如果用SQL拼凑,你几乎要改动所有的相关内容,而且还无法获取编译器的语法检查的好处,ORM在编译期间就可以检测出一些类型不匹配的问题。
2)界面和逻辑的真正分离改动计算逻辑,不需要改动界面,也就是实现MVC,MVP的模式,其实我们可以不用管这两个模式,我们只是在用ORM来读写数据库。
3)代码更幽雅,调试起来更容易,维护方便。
Linq技术之后,微软大力发展EntityFramework,不推荐在项目中使用。MS的优点是,它发现一项技术很有用,或为开发一个很得力的工具,最终它会做的很好,比如VisualStudio,Office,但是这是需要时间的,它要不停的学习,观察,改善,特别像API这类的东西,如果更新太快,会对项目产生较大的风险,实际项目中最需要的是稳定的API。MS打算要放弃的东西,它会慢慢减少资源,慢慢减少关注的次数,时间一长,最后就淡出了开发人员的视线。
NHibernate经过多年的发展,稳定,好用,有庞大的java社区(Hibernate)的支持,不愁遇到问题没有答案。
4工作流Workflow
目前微软推出了两个版本的工作流,.NET3.5和.NET4.0的,应该把它看成两个产品,并不是简单的版本升级。
ERP/MIS领域常见的需求有
1)采购单审批(如果条件)要求:
当金额大于等于500且采购员为A时,需要通过May的审批
当金额大于等于500且采购员为B时,需要通过Jack的审批
当金额小于500时,不需要通过审批,可直接过帐
2)当发生工程更改ECN时,要通知生产部重排计划,通知货仓安排发料。
像这种类型的需求,每个企业的要求都不一样,要达到定制的目的,又不想为每个不同的客户分别写代码,非用到工作流不可。你可以不选择用工作流,那就为不同的客户定制代码,也行。
MS的工作流也做成了中间件,你需要尽可能的override。
上一篇:软件工程与信息科学的关系
下一篇:协同软件实施特点与过程