旺财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一直是我的最爱,我觉得我会一直喜欢下去。

吉日嘎拉C#快速开发平台V4.0到V4.2升级记

目前我用的版本是4.0的,也有近2年没更新了,狠了狠心升级一下,没想到真的行动起来,也没那么难!

用了3天时间,将吉日嘎拉的代码升级到了4.2版本,并让原来的DotNet.WebApplication正常运行起来,比料想的顺利。这里简单记录一下升级中的心得。

使用到的工具:

1、BeyondCompare 试用版 – 比较程序文件

2、SQLDelta 14天试用版 – 比较数据库表结构变化(及数据变化)

3、VS2010 – 保证升级后WebApplication好用

4、MSSQL 2008 R2 – 标配数据库

最新代码的亮点:

1、分离出了DotNet.Model

2、分离出来DotNet.IService

3、DotNet.Business新增Redis缓存

4、DotNet.Utilities新增众多BaseSystemInfo参数和底层函数:数据库读写分离等

5、新增DotNet.UserCenter,用于其它程序如WebApp、安卓、苹果端调用

6、用户登录日志表完善、强大

7、数据库访问增加跟踪及底层文本日志

8、增加DotNet.MVC项目,BS端的用户及权限管理(还未研究)

相关截图:

1、数据库UserCenter更新

2、项目及解决方案截图

注意事项:

1、SqlDelta生成部署的代码后,还需要手动更新老记录中一些字段的值

UPDATE [UserCenterV40].[dbo].[BaseUser]
SET IsAdministrator=1,UserName='Administrator',NickName='Administrator'
WHERE UserName='Admin'
UPDATE BaseModule SET AuthorizedDays=0
UPDATE BaseUserLogOn SET OpenIdTimeout = GETDATE() 
UPDATE BaseUserContact SET MobileVerificationDate = GETDATE()

2、DotNet.WebApplication中有很多登录及读取权限的函数需要更新BaseSystemInfo.SystemCode

本文是升级记录的第1篇,后继会继续记录研究DotNet.MVC项目后的心得,敬请期待。

后记:请大家不要问我要源码,如需购买请直接联系吉日嘎拉,他的博客园的主页地址:http://www.cnblogs.com/jirigala/

1年多,近100次的版本更新,终于有所积累

不得不说,站在巨人的肩膀上,看的更远,走的更快。

前年的时候,买了吉日嘎拉的4998元的开发平台个人版本,去年公司里面采购了9998的企业版,一路上用下来,多是欣喜。公司里面的积累自然不用说,应对用户的需求,处理起来得心应手。

个人方面,去年初,答应邻居弄一个小库存管理软件,不知不觉1年多了,翻看了一下记录,光是Web端的版本发布就有83个,加上数据端的里程碑式的更新,总次数应该不下100次了。这都是周末、晚上、闲暇的时光积累出来的。从目前的使用情况看,用户体验、速度、多终端支持、报表方面还有众多亟待提升,但2/8原则来看,能支撑每月150万零售额的近10人团队,关键的问题已经解决。

从业10多年了,看着别人积累出各种产品,终于知道其中的不易,不过既然已经上路,就继续坚持,持续投入,日积月累,必有成效。

需求产生软件,你的需求是什么?你想解决什么问题?

为不会编程的人解决问题,为会编程的人节省时间!