最近为客户组织了一项C/S架构程序的开发培训,讲解C/S应用程序开发中需要注意的点。

我主要是做C/S方面的ERP/CRM程序开发,界面是用Windows Forms技术,有遗漏或错误的地方欢迎批评指正。

1 异常处理

为处理应用程序中的异常,需要增加以下代码。

Application.ThreadException += new ThreadExceptionEventHandler(eh.OnThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

2  Excel文件生成

我们以Infragistics Excel作为生成Excel的基础组件。它提供一套面向对象的模型以简化Exel文件操作。

excelWorkbook = new Workbook();
Worksheet currentWorksheet = this.excelWorkbook.Worksheets.Add("WorkSheet1");
foreach (var cell in currentWorksheet.GetRegion("A1:D1"))
{
    cell.CellFormat.Fill = CellFill.CreateSolidFill(Color.Gray);
    cell.CellFormat.Font.ColorInfo = new WorkbookColorInfo(Color.White);
}
currentWorksheet.Rows[0].Cells[0].Value = "Order ID";
currentWorksheet.Rows[0].Cells[1].Value = "Contact Name";
currentWorksheet.Rows[0].Cells[2].Value = "Shipping Address";
currentWorksheet.Rows[0].Cells[3].Value = "Order Date";

currentWorksheet.Columns[0].Width = 3000;
currentWorksheet.Columns[0].CellFormat.Alignment = HorizontalCellAlignment.Left;
currentWorksheet.Columns[1].Width = 7100;
currentWorksheet.Columns[2].Width = 3000;
currentWorksheet.Columns[2].CellFormat.Alignment = HorizontalCellAlignment.Left;
currentWorksheet.Columns[3].Width = 6100;

如果需要将网格数据导出为Excel,它专门为此提供一个导入格式对象,简单的调用以下代码即可达到目的。

using (System.Windows.Forms.SaveFileDialog dialog = new System.Windows.Forms.SaveFileDialog())
{
     dialog.DefaultExt = "xls";
     dialog.Filter = Shared.ExportToFileFilter;
     dialog.Title = Microsoft.Common.Shared.TranslateText("Export to File");
     dialog.FileName = this.Text;
     if (dialog.ShowDialog() != DialogResult.OK)
     {
          return;
     }
     if (dialog.FilterIndex == 1 || dialog.FilterIndex == 2)
     {
         using (UltraGridExcelExporter exporter = new UltraGridExcelExporter())
        {
            exporter.BandSpacing = BandSpacing.None;
            exporter.Export(gridFunction, dialog.FileName);
        }
     }
 }

3 第三方类库

为了简化第三方类库的部署,我在项目中直接将需要引用到的第三方类库作为嵌入的资源生成为一个程序集。

这样在部署时,根据需要将我生成的程序集复制到执行文件目录即可。同时需要增加一个程序集加载事件。

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
     return EmbeddedAssembly.Get(args.Name);
}

这个技巧来自于CodeProject,参考以下地址Load DLL From Embedded Resource

4 日志追踪

部署到生产环境中后,难免会出一些不可预料的异常。我使用SmartInspectPro来跟综这些问题。

官方网址是 http://www.gurock.com/smartinspect/

只需要下面简单的几行代码,就可以将程序中的异常信息或对象信息搜集起来,传送到日志查看工具中。

SiAuto.Si.Connections = "file(filename=c:\\log.sil)";
SiAuto.Si.Enabled = true;
SiAuto.Main.LogMessage("First Message!");

日志的内容可以写到文件,或是通过TCP或命名管道(named-pipes)发送到工具窗口中。

SiAuto.Si.Connections = string.Format("tcp(host={0},timeout=10000)", Microsoft.Common.Shared.ApplicationServer);

5 自动更新

以文件所在的位置来区分,我们考虑局域网,HTTP,FTP三种自动更新方式。.NET有许多自动更新组件,简单的列举。

http://wyday.com/wyupdate/

序号 名称 地址
1 AutoUpdater.NET https://autoupdaterdotnet.codeplex.com/
2 wyUpdate http://wyday.com/wyupdate/
3 Updater http://www.codeproject.com/Articles/9566/Updater
4 NetSparkle http://netsparkle.codeplex.com/
5 NAppUpdate https://github.com/synhershko/NAppUpdate
6 AutoUpdater https://autoupdater.codeplex.com/

微软本身也提供ClickOnce方式的更新方法,由于配置稍微麻烦我们并未采用。

6 版本检测

由于有多个客户的版本同时存在,我们在系统启动时,会检测当前文件夹中的所有文件的版本是否一致,如果不一致则抛出异常,终止执行。可参考如下的代码片段。

private static void VerifyAssembliesVersion()
{
    string[] files = Directory.GetFiles(Application.StartupPath, "Microsoft.EnterpriseSolution.*.dll", SearchOption.TopDirectoryOnly);
    Parallel.ForEach<string>(files, file =>
    {
       FileVersionInfo fileVersion = FileVersionInfo.GetVersionInfo(file);
       if (string.CompareOrdinal(fileVersion.FileVersion, AssemblyVersion.FileVersion) != 0)
          throw new AppException(string.Format("File version mismatch detected");                    }

7 源代码控制

我要提到的不是Team Foundation,SVN或Visual SourceSafe等源代码管理工具,而是如何控制客户正在使用的版本和程序员的开发版本。程序员的开发版本功能最多,同时也问题最多,许多新功能加入到程序中,没有经过完整的测试。

Team Foundation有一个分支管理功能,可以将客户正在使用的版本(正式版)看作是开发版本的(程序员开发)的一个子分支,每当在开发版中check in某项bug fix或feature并且经过完整测试后,将开发版本的变更集(changeset)合并到客户正在使用的分支版本中。

8 x86 x64 Any CPU的选择

现在.NET程序员真是太幸福了,编译时设定为Any CPU,JIT运行时根据机器的架构(x86,x64)生成相应的机器码。

我们的项目绝大多数情况下都选Any CPU作为生成架构。如果遇到一些编译依赖项它只有x86版本的程序集,这时我们考虑将依赖于这个x86的程序集的功能单独设计为一个DLL或EXE,这样整个项目还是以Any CPU架构来编译。

有时候出于安全原因,有一些代码以native语言来编写,比如C++,这时我们就分别生成两套(x86和x64)程序集,在部署时根据目标平台来部署相应架构的文件。

9 资源(图片,文档模板,标准报表)

为简化部署,我们将常用的资源项编译到一个程序集中。可参考以下代码提取嵌入的资源项。

 private static void ExtractEmbeddedResource(string resourceLocation, string output)
 {
   using (System.IO.Stream stream = Assembly.Load("Microsoft.Data").GetManifestResourceStream(resourceLocation))
   {
       using (BinaryReader r = new BinaryReader(stream))
       using (FileStream fs = new FileStream(output, FileMode.OpenOrCreate))
       using (BinaryWriter w = new BinaryWriter(fs))
       {
           w.Write(r.ReadBytes((int)stream.Length));
       }
   }
}

运行时我们从程序集中提取资源到硬盘临时文件夹,根据需要生成相应的文件返回给用户。

10 数据库访问

大型的项目离不开ORM,对象之间的运算与关联已不容易相处,如果还要去考虑数据读写,那程序的可维护性相对差很多。ORM带来的好处除了数据读写的完全解放,还有强类型的数据绑定。为此,我们的数据读写接口都是用Code Smith模板生成的,比如一个对象的读取方法

AccountEntity account = null;
using (DataAccessAdapter adapter = GetCompanyDataAccessAdapter(sessionId, companyCode))
{
    account = new AccountEntity(accountNo);
    bool found = adapter.FetchEntity(account, prefetchPath, null, fieldList);
    if (!found) throw new RecordNotFoundException(accountNo, "Invalid Account No.");
}

ORM带来另一个好处是强类型绑定,这样在设计时即可预知对象的类型和它的属性成员,方便做数据绑定。

ORM的第三个好处,可能是胜于直接写SQL语句(事务脚本模式)的地方,它会默认检测对象有哪些属性发生值改变,这样在保存对象时只会生成这些有发生值变更的SQL更新语句。许多同事甚至于我的上司都极度怀疑ORM的性能,我不确定他们是否真的验证过SQL语句(事务脚本模式)和ORM的性能比较。

11 性能

写的不合理的代码会导致性能问题,但不至于上升到怀疑技术的程度。微软的Entity Framework有那么多客户在用,难道这些客户的程序都是小规模,小应用吗? .NET代码的性能问题,我举例以下几个。

1) 主动要求GC进行垃圾回收会导致性能问题。

GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized);
最后在stackoverflow中找到回答是,任何时候都不应该调用此代码,注释以上代码后程序速度是快很多了。

2)  释放内存的代码会导致性能问题

[DllImport("kernel32.dll")]
private static extern bool SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);

具体原因可参考这里

 http://www.cnblogs.com/kex1n/archive/2011/01/26/2286427.html

3) 反射会影响性能

这个结论不是空口而谈,我是用ANTS Performance Profiler 8亲自测试反射和非反射的代码的运行时间得出的结论。

比如我想增加一个动态报表控件,根据系统安装的水晶报表的版本来加载水晶报表控件。于是有以下两种写法

//反射版
object  _crystalReportViewer;
_crystalReportViewer = ReflectionHelper.CreateObjectInstance(CrystalReportHelper.GetLongAssemblyName("CrystalDecisions.Windows.Forms", CrystalReportVersion), "CrystalDecisions.Windows.Forms.CrystalReportViewer");
//非反射版
CrystalDecisions.Windows.Forms.CrystalReportViewer  _crystalReportViewer;
_crystalReportViewer=new  CrystalDecisions.Windows.Forms.CrystalReportViewer();

之后调用Load方法,反射版的Load方法需要耗费的时间要比非反射版本多一倍左右。

ReflectionHelper.InvokeMethod(_crystalReportViewer, "Load", new System.Type[] {typeof (string), obj3.GetType()}, new object[] {path, obj3});

至于是否要用反射,我的结论是取决于应用场景。如果应用要求运行速度第一,可维护性其次。则应用最快的那种方法。比如有些医药行业的录单模块,对键盘的响应速度要求极高,这时用反射是不合适的。

反射可以通过预处理(pre-init,pre-load)等方式提高响应速度,这样可在性能和可维护性方面双赢。

4) 频繁的数据库读写会有性能问题

ORM实在是太方便了,各种计算和取值,只需要取到对象即可完成,代码的可复用性高。不过有时候会导致性能问题。

在包含很多逻辑操作时,为了取一个字段值而去频繁的构造对象是不合适的。比如在一个采购单列表功能中,为了取到采购单的部门编码对应的部门名称,我们频繁的去取数据库,并且以构造对象的方法来完成,这样会导致性能问题。正确的做法是构造DataTable来完成,构造一个包含1000行记录的DataTable要比构造1000个部门对象(DepartmentEntity)要快很多。

ORM另一个好处是按需分配,我们可以根据需要只读取部分字段的值,好比SELECT * 与SELECT 具体字段的区别。

参考以下的代码,为了提高性能,我们的系统绝大多数情况下都是以这种方式读取数据库字段。

IItemManager itemMan = ClientProxyFactory.CreateProxyInstance<IItemManager>();
ExcludeIncludeFieldsList fieldList = new ExcludeIncludeFieldsList(false);
fieldList.Add(ItemFields.Description);
fieldList.Add(ItemFields.StockUom);
fieldList.Add(ItemFields.ScrapRate);
fieldList.Add(ItemFields.DefBomNo);
fieldList.Add(ItemFields.ExtendedDesc);
fieldList.Add(ItemFields.RohsCompliance);
fieldList.Add(ItemFields.TempDescription);
fieldList.Add(ItemFields.Specification);
fieldList.Add(ItemFields.ColorCode);
ItemEntity item = itemMan.GetValidItem(Shared.CurrentUserSessionId, this.PartItemNo, null, fieldList, Shared.SystemParameter.TailorSinojoint);

ExcludeIncludeFieldsList 对象可以理解为SELECT语句中的具体字段的集合。

5) 控件的不合适操作会引起性能问题

设定选项卡控件的选中的方法,以下代码中第一种要比第二种快

//快一点
tabControl.SelectedTab=tabControl.Tabs[0];
//慢一些
tabControl.Tabs[0].Selected=true;

水晶报表控件的设定数据源连接的时候,ApplyLogonInfo要比SetConnection慢。

//快一点的代码
reportDocument.DataSourceConnections[0].SetConnection(
    connectionStringBuilder.DataSource,
    connectionStringBuilder.InitialCatalog,
    connectionStringBuilder.UserID,
    connectionStringBuilder.Password
);
//慢一些的代码
crDatabase = crReportDocument,Database
crTables = crDatabase.Tables
For Each crTable In crTables
      crTableLogOnInfo = crTable.LogOnInfo
      crTableLogOnInfo.ConnectionInfo = crConnectionInfo
      crTable.ApplyLogOnInfo(crTableLogOnInfo)
Next

12 事件销毁

C/S程序包含丰富的事件机制,我认为可用性要高于B/S程序。但是随之而来的是代码要比B/S慢。

当我们的程序中有太多事件时,我们需要在窗本释放时,将这些事件从委托链中移出。

protected override void ReleaseResources()
{
  this.btnPrintRouting.Click -= new System.EventHandler(this.btnPrintRouting_Click);
  this.btnPrintMaterialsList.Click -= new System.EventHandler(this.btnPrintMaterialsList_Click);
  this.btnSortMaterials.Click -= new System.EventHandler(this.btnSortMaterials_Click);
}
protected override void Dispose(bool disposing)
{
   if (disposing && components != null)
   {
        components.Dispose();
   }
   ReleaseResources();
   base.Dispose(disposing);
}
这个方法也是为了改善性能。
 

转载于:https://www.cnblogs.com/JamesLi2015/p/5801195.html

C/S架构应用程序开发培训笔记相关推荐

  1. Nokia Widget 应用开发培训笔记

    Nokia Widget 应用开发培训笔记 今天参加了Nokia在上海举办的widget开发技术培训会议,会议选址在上海巴黎春天大酒店,一家五星级酒店的300人会议室:培训间隙提供咖啡荼点,中午提供免 ...

  2. 视频教程-微信小程序开发培训教程-微信开发

    微信小程序开发培训教程 本人计算机专业,毕业工作已经10多年,从事过的行业有,安防,通讯,Gps定位,信息统计分析,互联网电商等,从事过的职位. 代码工程师(使用过的语言C#,PHP,Java),Ap ...

  3. 微信小程序开发培训-北京站

    微信小程序开发培训 小程序现在不是对外的,所以小程序里面的框架层.逻辑层.视图层代码都是保密的.只有通过了小程序的开发申请之后,才能下载小程序代码包,看到小程序代码层. 1.下面是微信公众平台的发展历 ...

  4. JEECG社区《微信小程序开发培训》视频

    为什么80%的码农都做不了架构师?>>>    JEECG社区<微信小程序开发培训>视频 课      程:  JEECG 微信小程序开发培训 讲      师:   周 ...

  5. GTK+图形化应用程序开发学习笔记(五)—组装盒、组合表、固定容器构件

    GTK+图形化应用程序开发学习笔记(五)-组装盒.组合表.固定容器构件 一.组装盒 组装盒(GtkBox)也称为组合构件.使用组装盒可以将多个构件放在一个容器中.容器可以把组装盒看作是一个构件.不像按 ...

  6. GTK+图形化应用程序开发学习笔记(一)—概述

    GTK+图形化应用程序开发学习笔记(一)-概述 一.什么是GNOME. GNOME的意思是"GNU Network Object Model Environment"(GNU网络对 ...

  7. java程序开发个人笔记_Java程序开发入门笔记

    Java程序开发入门笔记 如果你是这一个错误的话:错误:编码GBK的不可映射字符 这是因为我们编写的代码一般都是utf-8的格式而控制台用的不是utf-8格式所以就会出现中文输出的错误.这个时候我们可 ...

  8. .NET平台C/S架构应用程序开发核心技术总结(MyKTV点歌系统案例分析)

    .NET平台C/S架构应用程序开发核心技术总结(MyKTV点歌系统案例分析) 总结日期:2019年10月10日                                             ...

  9. 微信小程序开发个人笔记(2)

    微信小程序开发基础笔记 官方文档:微信官方文档 | 微信开放文档 (qq.com) 一.注册等 首先需要注册一下平台(Appid开发者d 和上传管理等都需要) 微信公众平台 (qq.com)(找不到就 ...

最新文章

  1. 百度李彦宏:人工智能是万物命脉
  2. Android-Presentation双屏异显-一看就懂篇
  3. Windows安装Python3
  4. Java里的容器存放的元素必须是1个对象.
  5. 【费用流】【线性规划】志愿者招募(luogu 3980)
  6. Pytorch中 .numpy() .item() .cpu() 区别
  7. 面试官:简单说说Java8中的HashMap到底有啥变化?
  8. python3多线程第三方库_Python3标准库:concurrent.futures管理并发任务池
  9. JAVA 没有重载运算符,那么 String 类型的加法是怎么实现的,以及String类型不可变的原因和好处...
  10. 【Matlab学习笔记】【函数学习】size参数
  11. 毛子说PostgreSQL 需要实现多主
  12. x80hd装linux,平板垃圾佬 篇五:台电也能打十个!x80hd双系统8寸的身体2G+32G的心胸~...
  13. ArcGIS地图制图
  14. 利用mongodb实现分布式WEB图片存储
  15. android支持的播放格式,android全格式多媒体播放器(一:ffmpeg移植)
  16. 辞旧迎新又一年(18年年终总结)
  17. Acer 4750 安装黑苹果_傻瓜式黑苹果安装神器
  18. bzoj 3375: [Usaco2004 Mar]Paranoid Cows 发疯的奶牛
  19. es的refresh和flush介绍
  20. 翟天临的噩梦:怎样用Python检测抄袭行为?

热门文章

  1. 获取汉字的首字母(转)
  2. 使用data uri将图片内嵌到html中
  3. JavaScript 的垃圾回收与内存泄露
  4. centos6.4 安装mysql
  5. 部署Symantec Antivirus 10.0网络防毒服务器之六
  6. mysql三锁,mysql锁机制之表锁(三)
  7. rsync远程同步的基本配置与使用
  8. 面试题: mysql数据库 已看1 简单的sql练习
  9. 多线程 -- 实现秒抓
  10. [翻译]AKKA笔记 - CHILD ACTORS与ACTORPATH -6