• 分享几个asp.net开发中的小技巧


    下面这几个,是在实际开发或阅读中发现的一些问题,有些甚至是有很多年开发人员写出的代码,也是很多人经常犯的错误。各位可以看看,你有没有躺着中枪。


    第一个,对整型变量进行非null判断。

    // a 是int型 (不是int?)
    if(a != null){
        //操作
    }

    个人点评:无意义判断,值类型永远不可能为null。

    第二个,用static来保持页面回发

    static int id;
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Request.QueryString["ID"] != null && Request.QueryString["ID"].ToString() != "")
        {
            id = Convert.ToInt32(Request.QueryString["ID"].ToString());
        }
    }

    个人点评:这个不解释,不知道怎么说。但最近还真就遇到了,而且也不是什么小项目,WebForm无服务器控件开发。

    第三个,用编程方式绑定数据控件时,数据源为DataSet时判断null而不判读DataSet内的Tables数。

    DataSet ds = bll.GetList(); 
    if (ds != null)
    {
        Repeater1.DataSource = ds;
        Repeater1.DataBind();
    }

    个人点评:当bll.GetList()返回的DataSet非null但里面没有包含数据表时,执行DataBind()方法时会报HttpException异常(IListSource 不包含任何数据源)。正确写法应该是

    DataSet ds = bll.GetList();
    if (ds != null && ds.Tables.Count > 0)
    {
        Repeater1.DataSource = ds;
        Repeater1.DataBind(); 
    }
    //
    DataSet ds = bll.GetList()??new DataSet();
    if (ds.Tables.Count > 0)
    {
        Repeater1.DataSource = ds;
        Repeater1.DataBind(); 
    }

    第四个,用编程方式绑定数据控件时,数据源为DataTable或List<T>时判断null。

    DataTable dt = bll.GetList();
    if (dt!=null)
    {
        Repeater1.DataSource = dt;
        Repeater1.DataBind();
    }

    个人点评:无意义判断,下面的写法没有任何问题,即使dt=null

    DataTable dt = bll.GetList();
    Repeater1.DataSource = dt;
    Repeater1.DataBind();

    第五个

    Model m = new Model(); 
    m = bll.GetModel(id);
    m.name;

    个人点评:以为只要声明时不为null,后面就不需要做非空非null判断了。万一第二步BLL层返回的model就为null呢?

    第六个,在Repeater1_ItemDataBound中写这样的代码

    Label lblPMID = (Label)e.Item.FindControl("lblPMID");
    if (lblPMID.Text != "")
    {
        //操作
    }

    个人点评:低效,无意义判断,很可能出现NullReferenceException(未将对象引用设置到对象的实例)异常。
    正确写法:

    Label lblPMID = e.Item.FindControl("lblPMID") as Label;
    if (lblPMID!=null && lblPMID.Text != "") //视里面使用情况决定是否判断lblPMID.Text为“”或空白
    {
        //操作
    }

    第七个

    string txtName = Request["txtName"] == null ? "" : Request["txtName"].ToString();
    string strWhere += "and ID=" + userId + ""; //userId是int
    if (txtName != "")
    {
        strWhere += " and NAME='" + txtName + "'"; 
    }
    strWhere += " order by id desc";
    //项目本身都是采用参数化查询的,这里是一些暴露给Web层的高级查询条件。

    个人点评:1、值类型和字符串拼接会隐式装箱,2、SQL注入危险。正确做法是userId.ToString()并且过滤txtName中特殊字符,限制字符串长度。
    注意,拼接SQL时过滤字符串并不能完全防止SQL注入,但很多时候在高级查询时拼接SQL是最简单也是最方便的,这时候过滤不应该只过滤一些指定的特殊字符,
    比如只过滤单引号,等号,大于/小于/等于,空格,括号之类的危险字符。应该对除中文字符、英文字母、和数字外的所有字符全部过滤掉(视情况而定)。
    并且严格限制字符串长度,一般查询时输入的关键字不会太长,如果用户输入的有空格,就拆分成多个条件。这样能尽可能的减小SQL注入的机会。


    最后,给大家分享几个小经验,虽说有些只是语法糖,但却可以帮助我们更高效编写或阅读代码。

    一、引用类型的null值很麻烦,因为类型为null时使用点运算符 (.)会报异常,所以经常要做非null判断,可以用?? null 合并运算符减少代码量。例如:

    //写法一
    int ID;
    if (Request.Form["ID"] != null && Request.Form["ID"].ToString() != "")
    {
    ID = Convert.ToInt32(Request.Form["ID"].ToString());
    }
    
    //写法二
    int id;
    if (int.TryParse(Request.Form["ID"]??"",out id))
    {
    
    }
    
    //方法一
    string userName2=string.Empty;
    if (Session["userName"]!=null)
    {
        userName2 = Session["userName"].ToString();
    }
    
    //方法二
    string userName1 = Session["userName"] == null ? "" : Session["userName"].ToString();
    
    //方法三
    string userName = (Session["userName"] ?? "").ToString();

    二、Web项目中的所有Session或cookie最好统一放到一个类中管理。最重要的目的是把Session中索引名独立出来管理,也就是除了本类外的所有页面都不要输入Session名。
    可能用语言表达不够直白,直接上代码。
    看到很多人是这样,包括网上流行的一些很常见的辅助类库。

     1     /// <summary>
     2     /// Session 操作类
     3     /// 1、GetSession(string name)根据session名获取session对象
     4     /// 2、SetSession(string name, object val)设置session
     5     /// </summary>
     6     public class SessionHelper
     7     {
     8         /// <summary>
     9         /// 根据session名获取session对象
    10         /// </summary>
    11         /// <param name="name"></param>
    12         /// <returns></returns>
    13         public static object GetSession(string name)
    14         {
    15             return HttpContext.Current.Session[name];
    16         }
    17         /// <summary>
    18         /// 设置session
    19         /// </summary>
    20         /// <param name="name">session 名</param>
    21         /// <param name="val">session 值</param>
    22         public static void SetSession(string name, object val)
    23         {
    24             HttpContext.Current.Session.Remove(name);
    25             HttpContext.Current.Session.Add(name, val);
    26         }
    27         /// <summary>
    28         /// 检测session是否存在
    29         /// </summary>
    30         /// <param name="name"></param>
    31         /// <returns></returns>
    32         public static bool CheckSession(string name)
    33         {
    34             try
    35             {
    36                 if (GetSession(name) == null)
    37                 {
    38                     return false;
    39                 }
    40                 else
    41                 {
    42                     return true;
    43                 }
    44             }
    45             catch
    46             {
    47                 return false;
    48             }
    49         }
    50     }
    View Code

    我想知道,这样做有什么实际意义么?而且HttpContext.Current还不做null检查。
    项目中还到处都是SessionHelper.SetSession("name"),SessionHelper.GetSession("name")这样编译器无法找到具体引用的代码,当你有很多页面用到这个会话后,
    你再想更改会话名称或删除这个会话那将是一场灾难,而且这样的代码多了还可能出现多个会话重名造成冲突,名称写错造成会话丢失。
    要克服以上问题,Web项目的Session会话你应该这样写

      1    /// <summary>
      2     /// 系统中Session会话管理
      3     ///<para>注意:该类使用Session,若在一般处理程序中调用该类方法必须实现 IRequiresSessionState 接口</para>
      4     /// </summary>
      5     public class SessionManager
      6     {
      7         private const string USER_LOGIN_MARK = "userModel";             //用户登录
      8         private const string USER_REGISTER_MARK = "registerModel";      //用户注册
      9         private const string CHECKSUM_MARK = "checksumModel";           //校验码
     10         private const string CAPTCHA_MARK = "captchaModel";             //验证码
     11 
     12         #region 用户登录会话
     13         /// <summary>
     14         /// 添加用户登录标识
     15         /// </summary>
     16         /// <param name="cardModel"></param>
     17         internal static void AddUserLoginMark(UserModel model)   //UserModel是用户对象
     18         {
     19             if (model == null)
     20             {
     21                 throw new ArgumentNullException("参数不能为Null");
     22             }
     23 
     24             HttpContext context = HttpContext.Current;
     25             if (context != null)
     26             {
     27                 context.Session[USER_LOGIN_MARK] = model;
     28             }
     29         }
     30 
     31         /// <summary>
     32         /// 移除用户登录标识
     33         /// </summary>
     34         internal static void RemoveUserLoginMark()
     35         {
     36             HttpContext context = HttpContext.Current;
     37             if (context != null)
     38             {
     39                 context.Session.Remove(USER_LOGIN_MARK);
     40             }
     41         }
     42 
     43         /// <summary>
     44         /// 获取当前登录用户对象
     45         /// </summary>
     46         /// <returns></returns>
     47         internal static UserModel GetUserLogin()
     48         {
     49             HttpContext context = HttpContext.Current;
     50             if (context != null)
     51             {
     52                 return context.Session[USER_LOGIN_MARK] as UserModel;
     53             }
     54 
     55             return null;
     56         }
     57         #endregion
     58 
     59         #region 用户注册会话
     60         /// <summary>
     61         /// 添加用户注册会话标识
     62         /// </summary>
     63         /// <param name="cardModel"></param>
     64         internal static void AddUserRegisterMark(RegisterModel model)
     65         {
     66             if (model == null)
     67             {
     68                 throw new ArgumentNullException("参数不能为Null");
     69             }
     70 
     71             HttpContext context = HttpContext.Current;
     72             if (context != null)
     73             {
     74                 context.Session[USER_REGISTER_MARK] = model;
     75             }
     76         }
     77 
     78         /// <summary>
     79         /// 移除用户注册会话标识
     80         /// </summary>
     81         internal static void RemoveUserRegisterMark()
     82         {
     83             HttpContext context = HttpContext.Current;
     84             if (context != null)
     85             {
     86                 context.Session.Remove(USER_REGISTER_MARK);
     87             }
     88         }
     89 
     90         /// <summary>
     91         /// 获取当前注册会话对象
     92         /// </summary>
     93         /// <returns></returns>
     94         internal static RegisterModel GetUserRegister()
     95         {
     96             HttpContext context = HttpContext.Current;
     97             if (context != null)
     98             {
     99                 return context.Session[USER_REGISTER_MARK] as RegisterModel;
    100             }
    101 
    102             return null;
    103         }
    104         #endregion
    105 
    106         #region 校验码会话(手机和邮件)
    107         /// <summary>
    108         /// 添加一个校验码会话
    109         /// </summary>
    110         internal static void AddChecksumMark(ChecksumModel model)
    111         {
    112             if (model == null)
    113             {
    114                 throw new ArgumentNullException("参数不能为Null");
    115             }
    116 
    117             HttpContext context = HttpContext.Current;
    118             if (context != null)
    119             {
    120                 context.Session[CHECKSUM_MARK] = model;
    121             }
    122         }
    123 
    124         /// <summary>
    125         /// 移除当前用户的校验码会话
    126         /// </summary>
    127         internal static void RemoveChecksumMark()
    128         {
    129             HttpContext context = HttpContext.Current;
    130             if (context != null)
    131             {
    132                 context.Session.Remove(CHECKSUM_MARK);
    133             }
    134         }
    135 
    136         /// <summary>
    137         /// 获取当前用户的校验码会话
    138         /// </summary>
    139         internal static ChecksumModel GetChecksum()
    140         {
    141             HttpContext context = HttpContext.Current;
    142             if (context != null)
    143             {
    144                 return context.Session[CHECKSUM_MARK] as ChecksumModel;
    145             }
    146 
    147             return null;
    148         }
    149         #endregion
    150 
    151         #region 验证码会话
    152         /// <summary>
    153         /// 添加一个验证码会话
    154         /// </summary>
    155         internal static void AddCaptchaMark(string c)
    156         {
    157             if (c == null)
    158             {
    159                 throw new ArgumentNullException("参数不能为Null");
    160             }
    161 
    162             HttpContext context = HttpContext.Current;
    163             if (context != null)
    164             {
    165                 context.Session[CAPTCHA_MARK] = c;
    166             }
    167         }
    168 
    169         /// <summary>
    170         /// 移除当前用户的验证码会话
    171         /// </summary>
    172         internal static void RemoveCaptchaMark()
    173         {
    174             HttpContext context = HttpContext.Current;
    175             if (context != null)
    176             {
    177                 context.Session.Remove(CAPTCHA_MARK);
    178             }
    179         }
    180 
    181         /// <summary>
    182         /// 获取当前用户的验证码会话
    183         /// </summary>
    184         internal static string GetCaptcha()
    185         {
    186             HttpContext context = HttpContext.Current;
    187             if (context != null)
    188             {
    189                 return context.Session[CAPTCHA_MARK] as string;
    190             }
    191 
    192             return null;
    193         }
    194 
    195         /// <summary>
    196         /// 检查验证码是否和服务器端一致(不区分大小写)
    197         /// </summary>
    198         /// <param name="code">用户输入的验证码</param>
    199         /// <returns></returns>
    200         public static bool ValidateCaptcha(string code)
    201         {
    202             if (string.IsNullOrWhiteSpace(code))
    203             {
    204                 throw new ArgumentNullException("参数不能为Null");
    205             }
    206             string code2 = GetCaptcha() ?? "";
    207             if (code.ToUpper() == code2.ToUpper())
    208             {
    209                 return true;
    210             }
    211             return false;
    212         }
    213         #endregion
    214     }
    View Code

    这样写,项目中使用时:
    登录成功就添加会话SessionManager.AddUserLoginMark(当前登录用户对象);
    页面登录检查时判断 SessionManager.GetUserLogin();返回是否为null就行。
    退出登录时SessionManager.RemoveUserLoginMark();即可。
    这样就只管调用就行,不用再去管Session名是什么,删除、更改也更方便,当然也不会出现Session重名现象了(如果这样都能整成会话重名的话,那你真成人才了)。

    当然,这样写也不是一点缺点都没有,和前一种相比,这种方法可能就不能做到一次书写,到处使用了,需要跟据当前项目具体灵活改动相应代码,但好处是很明显的。这样的方法同样适用于Cache和Cookie。

    三、最好不要用Request[]代替Request.Form[]和Request.QueryString[]。

    如果页面有很多Request.Form[]、Request.QueryString[]、Session[]最好在页面Page_Load中就把所有值取出来存放在变量中,并转换成需要的类型。
    满篇的Request.Form[]、Request.QueryString[]、Session[]编译器没法检查[]中的字符串,容易出错,影响阅读,还可能同一参数需要多次类型转换(这一条针对WebFrom无服务器控件开发时特别有用)。

    四、尽量使用 as 代替引用类型间转换(见上面第六个)

    这个大家都知道,但还是发现很多人在用强制转换,包括一些优秀的开源项目。


    五、RegisterClientScriptBlock、RegisterClientScriptInclude、RegisterStartupScript、RegisterOnSubmitStatement、RegisterClientScriptResource等方法要求前台页面必须有form服务器控件(<form runat="server"></form>)。
    也就意味着在WebForm无服务器控件开发时,这几个没什么用了(同样的还有Page.IsPostBack要小心了)。


    六、微软不推荐直接在后台使用Response.Write()输出JS,并且有的浏览器的确会造成页面变形。

    但发现很多人在用,包括一些很优秀的开源项目。我暂时用在前台加入<%= strMsg %>来接收后台传过来的消息,不知道各位都有什么好的方法。


    七、最后向大家分享一段自己写的小代码,为Repeater控件添加EmptyDataTemplate模板(EmptyDataTemplate在FooterTemplate之前)。

    原理很简单,默认添加的模板会出现在FooterTemplate之后,在RenderChildren()方法中给它们换下位置就行。
    代码如下:

     1 using System;
     2 using System.ComponentModel;
     3 using System.Web.UI;
     4 
     5 namespace MyRepeater
     6 {
     7     public class Repeater : System.Web.UI.WebControls.Repeater
     8     {
     9         [PersistenceMode(PersistenceMode.InnerProperty), Browsable(false), TemplateContainer(typeof(TemplateControl))]
    10         public ITemplate EmptyDataTemplate { get; set; }
    11 
    12         protected override void OnDataBinding(EventArgs e)
    13         {
    14             base.OnDataBinding(e);
    15 
    16             this.Controls.Clear();
    17             this.ClearChildViewState();
    18 
    19             this.CreateControlHierarchy(true);
    20             this.ChildControlsCreated = true;
    21 
    22             if (EmptyDataTemplate != null)
    23             {
    24                 if (this.Items.Count == 0)
    25                 {
    26                     EmptyDataTemplate.InstantiateIn(this);
    27                 }
    28             }
    29         }
    30 
    31         protected override void RenderChildren(HtmlTextWriter output)
    32         {
    33             if (HasControls())
    34             {
    35                 for (int i = 0; i < Controls.Count; i++)
    36                 {
    37                     if (this.FooterTemplate != null && this.Items.Count == 0 && EmptyDataTemplate != null)
    38                     {
    39                         if (i == Controls.Count - 2)
    40                         {
    41                             Controls[i + 1].RenderControl(output);
    42                             continue;
    43                         }
    44                         if (i == Controls.Count - 1)
    45                         {
    46                             Controls[i - 1].RenderControl(output);
    47                             continue;
    48                         }
    49                     }
    50                     Controls[i].RenderControl(output);
    51                 }
    52             }
    53         }
    54 
    55         protected override void Render(HtmlTextWriter output)
    56         {
    57             RenderChildren(output);
    58         }
    59     }
    60 }
    View Code

    暂时能想到的只有这么多,都是自己的经验之谈,当然也属于一家之言,如果有什么错误或不合理的,可以在下边留言给我或狠狠的踩上几脚。同时也希望大家能把自己的一些开发经验或技巧分享出来,供大家学习学习。

    2014-01-04补充

    错误修正

    第二段、第七个、为Repeater控件添加EmptyDataTemplate模板会导致Repeater的ItemDataBound事件处理执行两次。

    请注释掉下面这几行代码

                //this.Controls.Clear();
                //this.ClearChildViewState();
    
                //this.CreateControlHierarchy(true);
                //this.ChildControlsCreated = true;
    

     本来我的代码就参考了kdalan的这篇文章,他的文章中本来没有这几句的,但在看见msdn中的示例有这几句,当时也没多想就加上了,也不一在用,没出什么毛病。但前两天在调试的时候发现ItemDataBound事件会执行两次。为了查找为什么ItemDataBound事件为什么会多次执行,费了好大一番功夫,最后才排确定是this.CreateControlHierarchy(true)这句导致的。因为这几句是在MSDN上的示例中看到的,所有一直没怀疑它的正确性,在排错的过程中放到最后,耽误不少时间。其实最后想想,也大概知道了其中的原因,.net中的事件可以多次订,基类和子类都调用this.CreateControlHierarchy(true);会导致多次订阅ItemDataBound事件。为了证实我的猜想,专门建立了项目,对自定义的Repeater再次继承重写,在调用时ItemDataBound事件会被执行三次。测试项目我放到google code上,有兴趣的朋友可以下载看看,http://my-repeater.googlecode.com/svn

  • 相关阅读:
    Facebook的体系结构分析---外文转载
    简易的IOS位置定位服务
    【简易版】IOS仿periscope自制狂赞飘桃心
    The Linux Programming Interface
    freeswitch嵌入lua脚本
    Epoll Tutorial – epoll In 3 Easy Steps!
    基于freeswitch的webrtc与sip终端呼叫
    buildroot添加第三方库
    rfc
    TCP/UDP Server-Client implementation in C
  • 原文地址:https://www.cnblogs.com/weapon/p/3474272.html
Copyright © 2020-2023  润新知