1. 阿里云解决方案网首页
  2. 经验技巧

分享.NET ERP项目开发中应用到的重量级工具 选择合适的工具和资源,做项目效率高而且规范程度高

经过了近两年的发展和学习,技术方面有了长足的进步。而现在再来看那些工具和资源,自认为那时还处于工具系列的初级阶段,技术含量不高,也没有形成一个体系。这两天在整理自己的.NET通用开发平台和框架,认为很有必要更新一下自己的工具系列,作为一个负责任的作者,所以有了这一篇文章,希望能给你带来帮助。

这两年我一直从事WinForms的开发,所以没有关注到Web开发的工具。Web方面,一直想集成一个Web IIS到自己的WinForms程序中,这样做演示的时候,可以不用打开和配置IIS,也能测试HTTP方面的内容。对Web项目中的数据抓取也有一些经验和体会,可以参考我的知识管理系统Data Solution。

 

代码生成 Code Smith

折腾了很多代码生成器,自己也用C#+WinForms写过代码生成器,最后学乖了,做代码生成的工具,只选Code Smith,与代码生成相关的程序,只选择CodeDom。Code Smith有成熟的技术,数量庞大的模板库资源;而CdoeDom技术,一套源代码,可以同时生成C#和VB.NET两套代码。这是我选择它们的主要理由。

选择Code Smith的一个主要原因是,不仅仅Code Smith Studio做的稳定,强大,而且它支持以API的方式调用模板引擎,这种强大的可编程性,远远的把第三方的工具抛在后面。如下面的代码所示

CodeTemplateCompiler compiler = new CodeTemplateCompiler("..\\..\\StoredProcedures.cst");
compiler.Compile();
            
if (compiler.Errors.Count == 0)
{
   CodeTemplate template = compiler.CreateInstance();

   DatabaseSchema database = new DatabaseSchema(new SqlSchemaProvider(), @"Data Source=.\SQLEXPRESS;
       AttachDbFilename=PetShop.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True;");
   TableSchema table = database.Tables["Inventory"];
                
   template.SetProperty("SourceTable", table);
   template.SetProperty("IncludeDrop", false);
   template.SetProperty("InsertPrefix", "Insert");
                
   template.Render(Console.Out);
}
else
 {
    for (int i = 0; i < compiler.Errors.Count; i++)
    {
    Console.Error.WriteLine(compiler.Errors[i].ToString());
    }
}

在Code Smith Studio中制作模板,运行和调试模板。再以编程的方式把这个模板集成到自己的开发工具系列中。这项技术,我曾经写过一个Smith Builder,ERP代码生成器,它以集成的方式,把所有的代码生成的工具封装到一个环境。

image

2002年,有一篇文章《每个.NET开发人员应该下载的10种开发工具》中就提到它的名字,可见Code Smith在做代码生成方面是非常有经验的。Code Smith 6.0 已经于今年9月释放出来,但不如5.x系列的稳定。从5.3开始,Code Smith支持.NET 3.0/3.5的C#脚本代码,意味着可以应用Linq,C# 3.5的技术特性。

 

版本比较,合并 Beyond Compare

一个项目,通常会有多个客户同时在使用。一般建立的项目结构是,一个Development版,这是开发人员工作的版本,所有新特性,新功能,都会加在这个版本中,这也导致了这是bug最多的一个版本;同时,在特定的时间阶段,建立一个Build版本,比如2012年,公司会把ERP项目升级到4.0,于是所有的客户都会用到这个4.0版本。开发人员做好的程序功能,或是修改过的bug,都是建立在Development版本之上,要发送给客户的是Build版本,于是要把Development版中的功能或是bug fixed添加到Build版本中。你不能直接把Development版本的代码复制到Build版中,这样会导致客户使用的版本非常不稳定。Visual Studio Team Foundation创建了changset这个概念,表示对源代码的一个变更。

有一个常见的情景是,客户在Build版本中发现了问题,需要做出修复。程序员修改好Development版本,程序管理员负责把程序员修改好的Development版本的代码,合并(Merge)到Build版本中。Visual Studio Team Foundation有它的文件比较工具,但是也推荐Beyond Compare这个工具,作为默认的比较合并工具,可见它的功能非常强大。

image

左边是Development开发版的文件,右边是Build版。用这个工具,马上就看到了程序员在开发版中做出的修改,点击差异片段左上方的小箭头,代码就被复制到右边。这样就完成了把开发版的功能合并到客户构建版本中。

还有另一种情形,经常忘记了合并开发版本的代码,随着时间的累计,Development版和Build版的差异越来越大,合并起来也越来越困难。比如,客户那边的程序有问题,但是开发版没有问题,表示代码没有合并到Build版中。这个问题,一直是困扰着我们的团队,用肉眼去识别哪些代码是相同的,哪些是不同的,几乎是不可能。这个工具就是很好的帮手。

 

对象关系映射 LLBL Gen

在以前的技术选型中,我会看一下,ORM是否合适大型的项目这类型的文章。可是找到的大部分文章都是批判ORM的效率问题。经过这两年的理解和消化,如果要做大型的项目,我一定会向你推荐ORM,也就是这里提到的LLBL Gen。

image

LLBL Gen 从3.x起,支持NHibernate,也就是说一个LLBL Gen项目文件,可以同时生成LLBL Gen Runtime的项目,也可以生成NHibernate的项目。它内置了基于模板的代码生成器,你可以定制所有的模板与代码生成的结果。

比如,为了能在Visual Studio中,当鼠标悬停在一个实体或字段名上时,可以显示它的源数据库对象。

当鼠标悬停在实体名上时,可以显示实体对应的表名

clip_image015_thumb2

当鼠标悬停在字段上时,可以显示字段对应的表名

clip_image016_thumb1

你需要修改它的模板。打开Template Binding Viewers,按照如下的内容编辑模板

  1. entityIncludeAdatper.template #549行 增加
    ///Mapped on <[ CaseCamel TargetType ]> field: <[SourceObjectName]>.<[SourceColumnName]></summary>
  2. entityAdapter.template #31 增加
    ///Mapped on table: <[ElementTargetObjectName]></summary>

这样对它的模板定制,就达到了上面的效果。

另一个可以做出的定制是,为项目生成一个partial类。比如GBEMP员工表,对应于实体类EmployeeEntity,这是代码生成器自动生成的,我还需要创建一个Entity.Implmentation.cs文件,用于存放我加的自定义逻辑。你需要这样做

image

点击右边的Add Tasks,在SD.Task.Base中添加一个CreateAFolder的任务,参数名字为EntityImplementation.cs,再添加模板EntityImpemention.template,用Template Binding Viewers绑定到模板项中。看起来的效果是这样,添加模板ID与模板文件名的射影,如果模板不存在,它会提示你创建它。

image

虽然这么几句话,几个图片,就讲解完了我的思路和方法。我也没有夸奖它是如何的好,在这里仅仅以实例的方式,展示我理解的工具,以及我可以做的定制工作。微软的.NET Framework API几乎处处都考虑到了Extension,或是Customization,我这里给你推荐的LLBL Gen 作为ERP项目开发中的ORM框架,也是基于它的扩展性,灵活性。

好吧,总结一下,要做NHibernate开发,可以选择LLBL Gen工具作为代码生成器。如果已经是LLBL Gen 2.x的老用户,升级到新的3.x版本也很容易。
抱怨一下,3.0版本的LLBL Gen不稳定,EntityCollection在做设计时数据绑定时,经过提示Unable to cast object of type “Foundation.EntityCollection” to type “SD.LLBLGen.Pro.ORMSupportClasses.IEntityCollection2”, 我发誓我什么也没有改,你就让我无法绑定数据。升级到3.1后,这个问题就没有再发生过。看来,自己也给人家当了一回小白鼠,也不是老外的产品就一定没有bug,没有问题(issue)。要是被你发现了bug/issue,及时主动的给人家report issue,会是一个不小的鼓励。我自己在CodeProject看到文章后,发现了代码或是配置有问题,会及时留言,如果急,就给作者写email,通常一两天就回复了。一个是Russian,另一位是我这个Chinese,用英语(in English)交流技术细节,这时你就会感觉语言还真是个用于交流的好东西。

 

日志 跟踪 Log Trace

好吧,我承认你手头有很多很厉害的log/trace工具。著名的log4net, Enterpise Libaray的Log Application Block,还有大量的自己写的log/trace 工具库。可是,我还是不忍心把这个东西藏在硬盘里,不让你知道,这就是著名的TraceTool。CodeProject中有一篇文章,标题是《TraceTool 12.4: The Swiss-Army Knife of Trace》,你知道瑞士军刀很实用,是的,这里的Trace Tool也很实用。这个用Delphi写的实用工具,提供了任意编程语言的接口。

你可以在CodeProject的那篇文章中找到所有编程语言的接口例子,细节方面做的非常好。

image

在这个跟踪工具中看到的内容,是我下面的这几句代码所发射出来的

TTrace.Error.Send("Hello world");
TTrace.Warning.Send("Hello", "world");

TraceNode Hello = TTrace.Debug.Send("Hello"); // keep node reference

Hello.Send("world");           // use node to send sub trace

// or in one line :
TTrace.Debug.Send("Hello").Send("world");

不写log文件,也不写数据库。但是这个功能在跟踪你的程序运行流程方面,有很大的帮助。客户的服务器是不可以装上Debugger来给你发现问题的,除了报告异常的堆栈外,通过这个程序,把堆栈中的变量值输出来,及时查看。

为了可以输出当前的堆栈到trace窗口中,你可能需要这样的代码片段用于追踪call stack。

public static class Logger
{ 
    public static string CurrentStackDefaultLog()
    {
        // the true value is used to include source file info
        var l_CurrentStack = new System.Diagnostics.StackTrace(true);
        return l_CurrentStack.ToString();
    }
  
}

再深入到细节的地方,你可以参考CodeProject上的这篇《How to log the current call stack in .NET》。

我看中TraceTool的两点是,一是提供了图形化的界面可以看输出结果,二是支持任意的编程语言,连JavaScript都可以写日专输出,像这样的代码片段,用JavaScript写的trace代码。

var TTrace = new ActiveXObject("TraceToolCom.XTrace"); 
TTrace.Debug.Send("hello from jScript") ;

最后一点,它完全是free的,你不用到处找crack或是license key。即使你发现有问题了,下载它的源代码,跟踪进去看一下什么问题,也可以马上解决。

 

虚拟机 VMware Workstation

为了能建立一个真实的测试环境,我尝试过许多方法。在公司可以单独安装一台开发服务器,用于测试编译后的程序。在家里,通过笔记本,使用远程桌面,连接到台式机里面,进行测试。但是我要改一下设置,结果要在公司的测试服务器和自己家里的测试主机中,同时更改,总觉得有些不方便,于是就有这个虚拟机软件,构建一个尽可能真实的测试环境。

image

你可以也会选择Microsoft Virtual PC,或是Oracle Virtual Box,或者Windows Server 2008内置的虚拟机。都行,只要达到目的,尽可能建立一个测试环境就可以了。这里我有一些经验与你分享。

  1. 拒绝在虚拟机中安装Visual Studio,这是必要的。真实的环境,真的没有Visual Studio和它的Debugger。
  2. 同时准备两个虚拟机,一个x86,一个是x64的。这时,你就会发现Visual Studio中的Target=AnyCPU的作用。Visual C++中的Win32项目,只能在x86中跑。MSVCR.dll的含义是Microsoft Visual C++ Relase,表示是Release版本的动态连接库(在.NET中叫程序集Assembly),如果你的项目是用Debug方式编译的,它在这里是无法运行的,会提示的找不到动态连接库,Debug方式编译的要找MSVCD.dll。在制作Smith Builder的过程中,因为Code Smith的Target是x86的,所以,我要引用到它的程序集CodeSmith.Engine.dll,则我的项目的Target必须设置为x86,否则会提示bad image format。

Data Loader编译的Target是AnyCPU,同时我也提供它的x86版本,代码是这样写的

public class Program
{
    // Methods
    private static void Main(string[] args)
    {
        string str = Path.Combine(Application.StartupPath, "DataLoader.exe");
        Console.WriteLine("Trying to load and execute: {0} under x86", str);
        MethodInfo method = Assembly.LoadFile(str).GetType("Gui.Startup").GetMethod("Main", BindingFlags.NonPublic | BindingFlags.Static);
        object[] parameters = new object[] { args };
        method.Invoke(null, parameters);
    }
}

这个控制台项目的Target为x86,它反射DataLoader的启动窗体,并且调用它。以这种方式实现AnyCPU的程序以x86的方式运行。

3.  虚拟机只装操作系统的基本组件,再加上各种开发工具包的Runtime。如下面的这张图所示

image

这些基础的Runtime,都应该好好收藏起来,放在硬盘的角落里。

4. 如果你的硬盘空间充足,尽可能的准备x86和x64两种OS环境,两种Runtime,以充分测试。看下面的这张图片

image

为了测试到x64与x86中的问题,我把Visual C++的两种Runtime都下载到了硬盘中。这是一个细节方面的处理,也是为了能在交付程序之前,发现更多的问题。

 

5. .NET平台不是x86和x64的问题,而是旧的.NET程序集引用新版本的.NET程序集的问题。像下面这个图所示的,.NET 2.0的项目,引用了.NET 3.5的类型库,会抛出这个异常

image

项目的Target是.NET2.0,但是被引用的程序集编译是的Target是.NET 4.0,会出现上面的异常。强制引用的结果是,代码根本无法引有到程序集中定义的类型,编译出错。

为了看清楚一个程序集编译是依赖的.NET版本,你需要用Reflector 7打开这个程序集,查看References中的System.Core的版本,如下图所示,它是4.0,则表示要引用这个程序集的项目,它的Target至少是.NET 4.0。

image

我以为,把编译程序集的Target设为.NET 2.0是个好的选择,除非你真的需要应用.NET 3.5的特性。

在升级Workflow项目时,把.NET 3.5的工作流项目的Target改成.NET 4.0后,出现了很多未知的问题,改回去为.NET 3.5后,问题没了。在网上查到.NET 3.5的工作流和.NET 4.0的工作流,要当成完全不同的产品来对待。真遗憾,我肯定不会考虑升级工作流技术了,这样带来的问题和麻烦,远远超过你将要得到的好处。

 

这个分享工具系列的文章,差不多就介绍完了。在后继的文章中,我会介绍自己开发的工具,所做的的扩展Extension,以及最终把这些扩展和工具集,集成到一个环境中,做成一个ERP开发工具和框架平台,敬请期待。

原创文章,作者:admin,如若转载,请注明出处:https://www.aliyunsolution.com/2026.html