#0000:Talk is cheap,Show me the code.

做IT的,会写代码的都应该听说过这句名言,中文翻译:能说算不上什么,有本事就把你的代码给我看看。这句话是Linux 的创始人 Linus Torvalds 在 2000-08-25 给linux-kernel 邮件列表的一封邮件提到的。

作为一个程序员,如果不能产出高质量的代码,这的确有失本分。

那么怎么评价代码是否好?

那么又有没有办法让自己的代码派上更大的用场呢?

吴军《硅谷来信》里面提到了一个“工程师的5个等级”概念,我非常认同,我觉得这个工程师可以泛指为各行各业的工程师,不能仅仅看做IT工程师或程序员。

书中提到:能从第五级和第四级这种仅把“工作”做好,跳跃到“能独立设计和实现产品”,外加“在市场上获得成功”,得到客户认可,或者市场回报后,继续“设计并实现别人无法做出的产品”实现从第三级到第二级飞跃,持续努力和聚焦,进而能有机会进入第一级开创一个产业”。

简单一幅图,就给我们指明的方向。而令我倍感庆幸地是,这些年的经历,的确是这么个路径。

那么我写这篇文字的意图,想必大家也能猜到一二,我是想即日起开启新的分享:我过去几年在业余时间投入大量精力,经历了To B 产品打造和试水阶段,正打算将产品推向市场,帮助更多企业、帮助更多人,提高效率、增加收益、降低内耗,进而为中国社会高效运转和蓬勃发展,贡献自己的一份力量。

这个系列的分享每篇文章都会有一个编号,按照惯例从0开始,第一篇编号:#0000,如果能有幸写满一万篇,那此生也功德圆满了。

 

将旺财珠宝库存管理系统的前端ZUI升级到1.8.1

ZUI是一套开源的HTML5跨屏框架,是基于 Bootstrap 深度定制开源前端实践方案,帮助中国人快速构建现代跨屏应用。从2014年开始用于旺财珠宝库存管理系统的开发(历程#1),经历了电商零售版和实体批发版的2个版本的开发,较好地完成了客户在用户体验方面的要求,再次感谢这个位于青岛的开发团队,持续改善着这套UI,虽有VUE、React等后期前端之秀,但我独爱Bootstrap、钟爱ZUI。

旺财珠宝库存管理系统于2016年就基本稳定,适用于黄金、银等珠宝电商和实体批发企业使用。可喜的是我于2017年去申请了软件著作权,如果您或您周围的朋友有这方面需求,欢迎与我联系。由于时间仓库,目前没有搭建演示系统,但年内计划投入服务器托管,开放软件试用。需要强调的是:软件虽已成型,但可根据各企业需求定制开发。

由于系统一直使用的还是v1.5.0 – 2016-09,趁着周末升级到v1.8.1 – 2018-01-18。同时修复了一些已知的Bug。

还是那句话:看着别人积累出各种产品,终于知道其中的不易,不过既然已经上路,就继续坚持,持续投入,日积月累,必有成效。

 

devbridge/jQuery-Autocomplete 1.2.21升级到

旺财系列库存管理系统、WMS、进销存、订单系统、下单系统、客户门户等软件中使用的下拉自动完成组件是:devbridge/jQuery-Autocomplete,官网地址:https://github.com/devbridge/jQuery-Autocomplete/

目前用的v1.2.21是2015年6月的版本,今天升级到v1.4.7,更新日期2017年12月。

旺财2018的618小福利

今天端午节,我的旺财C#.NET代码生成器发布了V20180618版本,同时即日起公开DTcms4.旺财代码生成器免费版.20170926,有需要的朋友加我微信获取。

DTcms5版本可在淘宝购买:https://item.taobao.com/item.htm?spm=a230r.1.14.13.51f476cb2Kotft&id=545213785654&ns=1&abbucket=14#detail

在Global.asax中获取Session的注意事项

几年前给朋友珠宝公司开发过一套旺财珠宝库存管理系统,用得还是web Form老技术,但是更多的走Ashx+Ajax,但前端可是HTML5+jQuery+BootStrap等新技术,所以不论功能还是用户体验,都能很完美的满足用户要求(用户才不管你用的是什么技术,先进的和古老的都必须解决他的问题,然后还需要好用)。近期特别反馈说有些页面比较慢,我觉得用了几年了,数据库就近2个G了,可能是数据库查询的问题,也可能是程序执行的问题,也可能用户网络问题。数据库可以在服务器上用Sql Server Profiler进行查询分析,但页面上还得做点跟踪。于是就用Global.asax来实现,本来很方便的,但为了获取当前登录用户,需要在Global.asax中获取Session,花了点时间才搞定,记录下来分享一下。

本来想在Application_BeginRequest或者Session_Start里面获取的,可怎么也获取不到,于是翻看MSDN了解Global.asax的事件及执行顺序,在Application_AcquireRequestState中才获取到。

    protected DateTime StartDateTime;

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        //开始执行时间
        StartDateTime = DateTime.Now;
    }
    protected BaseUserInfo CurrentUserInfo;
    protected void Application_AcquireRequestState(object sender, EventArgs e)
    {
        if (Utilities.UserIsLogOn())
        {
            CurrentUserInfo = Utilities.GetUserInfo();
        }
    }

    protected void Application_EndRequest(object sender, EventArgs e)
    {
        DateTime endDateTime = DateTime.Now;
        TimeSpan ts = endDateTime - StartDateTime;
        //5秒以上的慢页面进行记录
        if (ts.TotalMilliseconds >= 5000)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("时间:" + endDateTime.ToString("yyyy-MM-dd hh:mm:ss fff") + ",当前请求URL:" + HttpContext.Current.Request.Url + ",请求的参数为:" + HttpContext.Current.Request.QueryString + ",页面加载的时间:" + ts.TotalMilliseconds + " 毫秒");
            if (CurrentUserInfo != null)
            {
                sb.Append(",用户:" + CurrentUserInfo.UserName);
            }
            FileUtil.WriteMessage(sb.ToString(), BaseSystemInfo.StartupPath + "//Log//Slow/" + DateTime.Now.ToString(BaseSystemInfo.DateFormat) + ".txt");
        }

    }

通过上述代码就可方便的获取哪些页面比较慢,何时、何人、何参数、何地(IP)发生的。

2018-05-11 03:33:18 947:[当前请求URL:Modules/WMS/ItemMaster/ItemMasterPlan.aspx;请求的参数为:;页面加载的时间:8151.3672 毫秒]
2018-05-11 04:09:25 181:[当前请求URL:Modules/WholesaleWMS/tools/WholesaleBPStatement.ashx?action=RefreshStatement;请求的参数为:action=RefreshStatement;页面加载的时间:19720.7031 毫秒]
2018-05-11 05:18:10 486:[当前请求URL:Modules/WMS/OutboundOrderLine/OutboundOrderLineListSummary.aspx;请求的参数为:;页面加载的时间:16742.1875 毫秒]

2018-05-12 10:33:59 305:[当前请求URL:Modules/WMS/PurchaseDemand/PurchaseDemandAdmin.aspx;请求的参数为:;页面加载的时间:9375.9765 毫秒]
2018-05-12 10:49:19 497:[当前请求URL:Modules/WMS/ItemMaster/ItemMasterPlan.aspx;请求的参数为:;页面加载的时间:5278.3203 毫秒]
2018-05-12 01:24:36 673:[当前请求URL:Modules/WMS/InboundOrderLine/InboundOrderLineListReport.aspx;请求的参数为:;页面加载的时间:11416.0156 毫秒]
2018-05-12 01:24:42 045:[当前请求URL:Modules/WMS/InboundOrderLine/InboundOrderLineListReport.aspx;请求的参数为:;页面加载的时间:6209.9609 毫秒]
2018-05-12 01:24:42 611:[当前请求URL:Modules/WMS/InboundOrderLine/InboundOrderLineListReport.aspx;请求的参数为:;页面加载的时间:10142.5781 毫秒]
2018-05-12 04:39:35 251:[当前请求URL:Modules/WMS/OutboundOrderLine/OutboundOrderLineListSummary.aspx;请求的参数为:;页面加载的时间:16623.0469 毫秒]
2018-05-12 04:51:31 401:[当前请求URL:Modules/WMS/OutboundOrderLine/OutboundOrderLineListSummary.aspx;请求的参数为:;页面加载的时间:16648.4375 毫秒]
2018-05-12 04:57:15 362:[当前请求URL:Modules/WMS/OutboundOrderLine/OutboundOrderLineListSummary.aspx;请求的参数为:;页面加载的时间:16552.7343 毫秒]

最后附上MSDN上对Global.asax的解释:

按执行顺序来解释一下Global.asax.cs中相应的事件处理方法的含义

  1. Application_BeginRequest:BeginRequest是在收到Request时第一个触发的事件,这个方法自然就是第一个执行的了。
  2. Application_AuthenticateRequest:当安全模块已经建立了当前用户的标识后执行。
  3. Application_AuthorizeRequest:当安全模块已经验证了当前用户的授权时执行。
  4. Application_ResolveRequestCache:当ASP.NET完成授权事件以使缓存模块从缓存中为请求提供服务时发生,从而跳过处理程序(页面或者是WebService)的执行。这样做可以改善网站的性能,这个事件还可以用来判断正文是不是从Cache中得到的。
  5. Application_AcquireRequestState:当ASP.NET获取当前请求所关联的当前状态(如Session)时执行(真是拗口啊,msdn上就这样写的,我自己想不出什么好句子了)。
  6. Application_PreRequestHandlerExecute:当ASP.Net即将把请求发送到处理程序对象(页面或者是WebService)之前执行。这个时候,Session就可以用了。
  7. Application_PostRequestHandlerExecute:当处理程序对象(页面或者是WebService)工作完成之后执行。
  8. Application_ReleaseRequestState:在ASP.NET执行完所有请求处理程序后执行。ReleaseRequestState事件将使当前状态数据被保存。
  9. Application_UpdateRequestCache:在ASP.NET执行完处理程序后,为了后续的请求而更新响应缓存时执行。
  10. Application_EndRequest:同上,EndRequest是在响应Request时最后一个触发的事件,这个方法自然就是最后一个执行的了。

再附上两个无顺序的,随时都可能执行的

  1. Application_PreSendRequestHeaders:向客户端发送Http标头之前执行。
  2. Application_PreSendRequestContent:向客户端发送Http正文之前执行。

C#开发代码规范中PascalCase和camelCase的两个有用的方法类

#region 代码规范风格化
        /// <summary>
        /// 转换为Pascal风格-每一个单词的首字母大写
        /// </summary>
        /// <param name="fieldName">字段名</param>
        /// <param name="fieldDelimiter">分隔符</param>
        /// <returns></returns>
        public static string ConvertToPascal(string fieldName, string fieldDelimiter)
        {
            string result = string.Empty;
            if (fieldName.Contains(fieldDelimiter))
            {
                //全部小写
                string[] array = fieldName.ToLower().Split(fieldDelimiter.ToCharArray());
                foreach (var t in array)
                {
                    //首字母大写
                    result += t.Substring(0, 1).ToUpper() + t.Substring(1);
                }
            }
            else if (string.IsNullOrWhiteSpace(fieldName))
            {
                result = fieldName;
            }
            else if (fieldName.Length == 1)
            {
                result = fieldName.ToUpper();
            }
            else
            {
                result = fieldName.Substring(0, 1).ToUpper() + fieldName.Substring(1);
            }
            return result;
        }
        /// <summary>
        /// 转换为Camel风格-第一个单词小写,其后每个单词首字母大写
        /// </summary>
        /// <param name="fieldName">字段名</param>
        /// <param name="fieldDelimiter">分隔符</param>
        /// <returns></returns>
        public static string ConvertToCamel(string fieldName, string fieldDelimiter)
        {
            //先Pascal
            string result = ConvertToPascal(fieldName, fieldDelimiter);
            //然后首字母小写
            if (result.Length == 1)
            {
                result = result.ToLower();
            }
            else
            {
                result = result.Substring(0, 1).ToLower() + result.Substring(1);
            }
            
            return result;
        }
        #endregion

近期为统一Oracle数据库下大写表名和字段,以及下划线_分隔符的特点,升级了旺财C#.NET代码生成器,将规范化的代码写了2个方法用于Camel和Pascal风格化,用于有表字段分隔符的场景。

骆驼拼写法,英文名CamelCase。分为两种:

第一个词的首字母小写,后面每个词的首字母大写,叫做“小骆驼拼写法”(lowerCamelCase);

第一个词的首字母,以及后面每个词的首字母都大写,叫做“大骆驼拼写法”(UpperCamelCase),又称“帕斯卡拼写法”(PascalCase)

两者核心差别:PascalCase第一个单词的首字母大写,而CamelCase第一个单词的首字母小写。

还是连接池的问题,终于搞定了

上个月中旬提到过被Web.config中数据库连接池 Max Pool Size的问题折腾了,但是增加到200个最大连接池,还是会报错:

System.InvalidOperationException: Timeout expired.  The timeout period elapsed prior to obtaining a connection from the pool.   This may have occurred because all pooled connections were in use and max pool size was reached.

本打算借助微软的免费工具Debug Diagnostic Tool v2 Update 2,搞了半天不太会用。为了省事,借助RedGate的免费14天试用的ANTS Memory Profiler free trial,终于找到问题所在。原来是连接泄露了,在connection连接后未及时使用dispose()或close()进行关闭。

在升级改进吉日嘎拉DotNet数据访问层DotNet.Utilities时,本来继承了IDisposable接口,后改为IDbHelper,但并未启用手动关闭连接。原来的IDisposable接口的主要用途是释放非托管资源。当不再使用托管对象时,垃圾回收器会自动释放分配给该对象的内存。

至此,数据库连接池的报错终于完全修复。

困惑了2年多的C#问题,终于解决了

翻了一下QQ聊天记录,其实这个问题也是困扰吉日嘎拉的问题,2015年我曾经就此问题跟他交流过。

在更新语句中和条件中有相同的参数问题,造成报错:

 The variable name '%.*ls' has already been declared. Variable names must be unique within a query batch or stored procedure.  

曾经我是先判断条件语句,获取到主键ID,然后再根据主键ID为条件进行更新,多了一次数据库读取,折中处理了好几年。

这次再写类似的程序,实在觉得这么搞太费劲,索性花了几个小时,最终的思路就是即便是同名的字段,条件语句的参数自动改名:增加后缀或前缀。

这么一改,条件的参数就自动增加了后缀Where,就跟更新字段的参数不重名了。当然了你也可以自定义自己的后缀或者前缀。

最终时隔2年多,将此更改跟吉日嘎拉再次沟通,也解决了他的困惑,皆大欢喜。

有在用吉日嘎拉底层DotNet.Common数据读写层的朋友,请拿去不谢。

C#中启用托管的Oracle.ManagedDataAccess访问Oracle数据库

上个月有个项目从基于Oracle数据库的Infor ERP LN系统中多表关联查询记录,遇到一张600多万的记录表,造成本来MSSQL的Linked Server方式好用的SQL频频超时。难道是服务器与服务器之间的网络有问题?还是MSSQL 2008 R2的问题?也朝这个方向研究了一阵,后来想想算了,还是直接连Oracle吧。

原来偷懒的技术债务,不还不行了。

于是启用托管方式的Oracle.ManagedDataAccess.dll,并从老版本4.121.2.20150926的dll更新到了4.122.1.20170524,升级了原来吉日嘎拉的数据访问底层,web.config等,半天搞定老sql的替换,测试跑下来速度飞快了。

我一直很喜欢写程序,你呢?

我是1999年上大学的时候才接触计算机,那时候上网还叫“冲浪”。第一次去学校的机房,视觉、身心被震撼到,深信计算机的未来很美好,于是基本来放弃了本专业(电气工程及自动化),大部分时间都用在了计算机方面的学习,最初对计算机硬件及网络感兴趣,本来打算在盯着这个领域发展的,还因此去读了MCSE,CCNA。但是后来学了C语言,学了SQL,考了MCDBA,就发现还是喜欢编程,从硬变软了。 

记得当初C语言考了90多分吧,算是高分。当时流行考计算机等级考试,我是考了C语言二级之后,又去考了QBasic,后来学什么网页三剑客,做网页,学html javascript,学SQL,还有flash的Action Script写东西。大三学汇编(必修课),大四的时候把计算机等级考试的四级也过了,这个4级对计算机系不算什么,但对我们学院,我就记得我们那届全院就我一个过了。 

2003年毕了业之后呢,虽然也做网络工程师的硬件、网络方面的工作,但后面还是写网页比较多,当时用的是Asp,使用vb script语言写的。

2004年的时候跳槽,获得Web Developer的职位,主要是内部OA开发,基于Asp+MSSQL,2005年还去研究一个类似于java的语言Coldfusion,当时还索性把他的coldfusion.cn域名注册了,等到现在也没见到Adobe来收购。同期开始接触ERP,开始熟练使用Access+sql+VBA。 

2005年下半年换工作,还是以开发为主,不仅有Web开发,也开始做BaaN的4GL开发。大概2008年开始,从VS2005升级到VS2008,开始转向C# .NET,就这样一路走到今天。 

虽然近几年很多日常工作是实施上线、技术支持、项目管理方面的,但是业余时间我还是一如既往的热爱着编程,从企业软件到电子商务,再到移动互联网开发,一直不停的坚持着。

我觉得喜欢写程序的一个原因是:太懒!发现周围的一切效率低下,就希望通过写程序来解决。在解决实际问题的同时,也一直努力提高开发效率,节约时间,让生命少一些加班,多一些色彩和可能。

但事实上绝大多数开发团队、开发成员的开发效率普遍低下,大部分时间并非用于关键的开发,而是浪费在重复工作等毫无意义的事情上。于是3年前,我开始将我自己的想法、结合几位老师(吉日嘎拉等)的经验,积累出一套经受住实战考验的开发平台和开发辅助工具:旺财C# .NET代码生成器,近日拿到了软件著作权,终算有所小成。

2016年开始,我将代码生成器扩展支持当下国内最流行的C# .NET开源CMS:DTcms,在淘宝开店并销售,积累了几十位付费用户,同时积累了一批有相同理念和价值观的程序员、软件老板朋友。

因为互联网领域Java比.NET更受欢迎,我会因此去关注、学习一些Java基础,也会关心一下Python等大数据领域的流行语言,但.NET一直是我的最爱,我觉得我会一直喜欢下去。