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

时间:2022-02-05 21:08:34

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

第一个,对整型变量进行非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 > )
{
Repeater1.DataSource = ds;
Repeater1.DataBind();
}
//或
DataSet ds = bll.GetList()??new DataSet();
if (ds.Tables.Count > )
{
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名。
可能用语言表达不够直白,直接上代码。
看到很多人是这样,包括网上流行的一些很常见的辅助类库。

     /// <summary>
/// Session 操作类
/// 1、GetSession(string name)根据session名获取session对象
/// 2、SetSession(string name, object val)设置session
/// </summary>
public class SessionHelper
{
/// <summary>
/// 根据session名获取session对象
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static object GetSession(string name)
{
return HttpContext.Current.Session[name];
}
/// <summary>
/// 设置session
/// </summary>
/// <param name="name">session 名</param>
/// <param name="val">session 值</param>
public static void SetSession(string name, object val)
{
HttpContext.Current.Session.Remove(name);
HttpContext.Current.Session.Add(name, val);
}
/// <summary>
/// 检测session是否存在
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static bool CheckSession(string name)
{
try
{
if (GetSession(name) == null)
{
return false;
}
else
{
return true;
}
}
catch
{
return false;
}
}
}

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

    /// <summary>
/// 系统中Session会话管理
///<para>注意:该类使用Session,若在一般处理程序中调用该类方法必须实现 IRequiresSessionState 接口</para>
/// </summary>
public class SessionManager
{
private const string USER_LOGIN_MARK = "userModel"; //用户登录
private const string USER_REGISTER_MARK = "registerModel"; //用户注册
private const string CHECKSUM_MARK = "checksumModel"; //校验码
private const string CAPTCHA_MARK = "captchaModel"; //验证码 #region 用户登录会话
/// <summary>
/// 添加用户登录标识
/// </summary>
/// <param name="cardModel"></param>
internal static void AddUserLoginMark(UserModel model) //UserModel是用户对象
{
if (model == null)
{
throw new ArgumentNullException("参数不能为Null");
} HttpContext context = HttpContext.Current;
if (context != null)
{
context.Session[USER_LOGIN_MARK] = model;
}
} /// <summary>
/// 移除用户登录标识
/// </summary>
internal static void RemoveUserLoginMark()
{
HttpContext context = HttpContext.Current;
if (context != null)
{
context.Session.Remove(USER_LOGIN_MARK);
}
} /// <summary>
/// 获取当前登录用户对象
/// </summary>
/// <returns></returns>
internal static UserModel GetUserLogin()
{
HttpContext context = HttpContext.Current;
if (context != null)
{
return context.Session[USER_LOGIN_MARK] as UserModel;
} return null;
}
#endregion #region 用户注册会话
/// <summary>
/// 添加用户注册会话标识
/// </summary>
/// <param name="cardModel"></param>
internal static void AddUserRegisterMark(RegisterModel model)
{
if (model == null)
{
throw new ArgumentNullException("参数不能为Null");
} HttpContext context = HttpContext.Current;
if (context != null)
{
context.Session[USER_REGISTER_MARK] = model;
}
} /// <summary>
/// 移除用户注册会话标识
/// </summary>
internal static void RemoveUserRegisterMark()
{
HttpContext context = HttpContext.Current;
if (context != null)
{
context.Session.Remove(USER_REGISTER_MARK);
}
} /// <summary>
/// 获取当前注册会话对象
/// </summary>
/// <returns></returns>
internal static RegisterModel GetUserRegister()
{
HttpContext context = HttpContext.Current;
if (context != null)
{
return context.Session[USER_REGISTER_MARK] as RegisterModel;
} return null;
}
#endregion #region 校验码会话(手机和邮件)
/// <summary>
/// 添加一个校验码会话
/// </summary>
internal static void AddChecksumMark(ChecksumModel model)
{
if (model == null)
{
throw new ArgumentNullException("参数不能为Null");
} HttpContext context = HttpContext.Current;
if (context != null)
{
context.Session[CHECKSUM_MARK] = model;
}
} /// <summary>
/// 移除当前用户的校验码会话
/// </summary>
internal static void RemoveChecksumMark()
{
HttpContext context = HttpContext.Current;
if (context != null)
{
context.Session.Remove(CHECKSUM_MARK);
}
} /// <summary>
/// 获取当前用户的校验码会话
/// </summary>
internal static ChecksumModel GetChecksum()
{
HttpContext context = HttpContext.Current;
if (context != null)
{
return context.Session[CHECKSUM_MARK] as ChecksumModel;
} return null;
}
#endregion #region 验证码会话
/// <summary>
/// 添加一个验证码会话
/// </summary>
internal static void AddCaptchaMark(string c)
{
if (c == null)
{
throw new ArgumentNullException("参数不能为Null");
} HttpContext context = HttpContext.Current;
if (context != null)
{
context.Session[CAPTCHA_MARK] = c;
}
} /// <summary>
/// 移除当前用户的验证码会话
/// </summary>
internal static void RemoveCaptchaMark()
{
HttpContext context = HttpContext.Current;
if (context != null)
{
context.Session.Remove(CAPTCHA_MARK);
}
} /// <summary>
/// 获取当前用户的验证码会话
/// </summary>
internal static string GetCaptcha()
{
HttpContext context = HttpContext.Current;
if (context != null)
{
return context.Session[CAPTCHA_MARK] as string;
} return null;
} /// <summary>
/// 检查验证码是否和服务器端一致(不区分大小写)
/// </summary>
/// <param name="code">用户输入的验证码</param>
/// <returns></returns>
public static bool ValidateCaptcha(string code)
{
if (string.IsNullOrWhiteSpace(code))
{
throw new ArgumentNullException("参数不能为Null");
}
string code2 = GetCaptcha() ?? "";
if (code.ToUpper() == code2.ToUpper())
{
return true;
}
return false;
}
#endregion
}

这样写,项目中使用时:
登录成功就添加会话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()方法中给它们换下位置就行。
代码如下:

 using System;
using System.ComponentModel;
using System.Web.UI; namespace MyRepeater
{
public class Repeater : System.Web.UI.WebControls.Repeater
{
[PersistenceMode(PersistenceMode.InnerProperty), Browsable(false), TemplateContainer(typeof(TemplateControl))]
public ITemplate EmptyDataTemplate { get; set; } protected override void OnDataBinding(EventArgs e)
{
base.OnDataBinding(e); this.Controls.Clear();
this.ClearChildViewState(); this.CreateControlHierarchy(true);
this.ChildControlsCreated = true; if (EmptyDataTemplate != null)
{
if (this.Items.Count == )
{
EmptyDataTemplate.InstantiateIn(this);
}
}
} protected override void RenderChildren(HtmlTextWriter output)
{
if (HasControls())
{
for (int i = ; i < Controls.Count; i++)
{
if (this.FooterTemplate != null && this.Items.Count == && EmptyDataTemplate != null)
{
if (i == Controls.Count - )
{
Controls[i + ].RenderControl(output);
continue;
}
if (i == Controls.Count - )
{
Controls[i - ].RenderControl(output);
continue;
}
}
Controls[i].RenderControl(output);
}
}
} protected override void Render(HtmlTextWriter output)
{
RenderChildren(output);
}
}
}

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

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

分享几个asp.net开发中的小技巧的更多相关文章

  1. ios开发中的小技巧

    在这里总结一些iOS开发中的小技巧,能大大方便我们的开发,持续更新. UITableView的Group样式下顶部空白处理 //分组列表头部空白处理 UIView *view = [[UIViewal ...

  2. iOS开发中调试小技巧

    对于软件开发而言,调试是必须学会的技能,重要性不言而喻.对于调试的技能,基本上是可以迁移的,也就是说你以前在其他平台上掌握的很多调试技巧,很多也是可以用在iOS开发中.不同语言.不同IDE.不同平台的 ...

  3. iOS - 开发中调试小技巧

    对于软件开发而言,调试是必须学会的技能,重要性不言而喻.对于调试的技能,基本上是可以迁移的,也就是说你以前在其他平台上掌握的很多调试技巧,很多也是可以用在iOS开发中.不同语言.不同IDE.不同平台的 ...

  4. Android开发中的小技巧

    转自:http://blog.csdn.net/guxiao1201/article/details/40655661 简单介绍: startActivities (Intent[] intents) ...

  5. Android 开发中常用小技巧

    TextView中的getTextSize返回值是以像素(px)为单位的, 而setTextSize()是以sp为单位的. 所以如果直接用返回的值来设置会出错,解决办法是 用setTextSize() ...

  6. 在这里总结一些iOS开发中的小技巧,能大大方便我们的开发,持续更新。

    UITableView的Group样式下顶部空白处理 //分组列表头部空白处理 UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0 ...

  7. js开发中常用小技巧

    1.获取指定范围内的随机数 function getRadomNum(min,max){ return Math.floor(Math.random() * (max - min + 1)) + mi ...

  8. 一些IOS开发中的小技巧

    1.打包后提交报错误 错误信息:ERROR ITMS-90035: "Invalid Signature. Code object is not signed at all. The bin ...

  9. Android开发中这些小技巧

    http://blog.csdn.net/guxiao1201/article/details/40655661 http://blog.csdn.net/guxiao1201/article/det ...

随机推荐

  1. 查看 table&comma;view&comma;sp的定义

    1, 查看用户创建的Proc,View, UDF,trigger 的定义 sys.sql_modules Returns a row for each object that is an SQL la ...

  2. Intellij Idea中运行tomcat 报內存溢出 解决方案 火龙战士 火龙战士

    在Run/Debug configuration 的你要运行行的tomcat里面的 vm options里面输入 -server -XX:PermSize=128M -XX:MaxPermSize=2 ...

  3. Redhat系统网络配置

    1.RedHat系统的网络配置文件/etc/sysconfig/network-scirpts/ifcfg-<interface-name>文件 DEVICE=eth0           ...

  4. Hadoop基础教程之搭建开发环境及编写Hello World

    整个Hadoop是基于Java开发的,所以要开发Hadoop相应的程序就得用JAVA.在linux下开发JAVA还数eclipse方便. 1.下载 进入官网:http://eclipse.org/do ...

  5. 复旦大学EWP菁英女性课程(复旦卓越女性课程改版后第一期) &lowbar;复旦大学、女性课程、高级研修班、心理学、EWP&lowbar;培训通课程

    复旦大学EWP菁英女性课程(复旦卓越女性课程改版后第一期) _复旦大学.女性课程.高级研修班.心理学.EWP_培训通课程 复旦大学EWP菁英女性课程(复旦卓越女性课程改版后第一期)    学      ...

  6. CSS上下左右居中的几种方法

    1.absolute,margin: auto .container { position: relative; } .content { position: absolute; margin: au ...

  7. oracle之 any、some、all 解析

    举几个例子来说明ALL和ANY的用法 1. SELECT * FROM TABLEA WHERE FLD > ALL(SELECT FLD FROM TABLEA) 这相当于 SELECT * ...

  8. 移动端rem flexible方案

    一.px 自动转换为rem sublim Text3 下载本项目,比如:git clone https://github.com/flashlizi/cssrem 进入packages目录(在Subl ...

  9. zabbix添加ceph监控

    应用背景: 网上监控ceph集群的资料不算多,git上有个开源的监控项目,是跟zabbix结合的,主要包含一个shell写的脚本和zabbix监控模板,拿来测试小记一下. 开源地址: https:// ...

  10. Android学习笔记&lpar;38&rpar;&colon;Handler消息传递处理机制

    先说为什么须要Handler: Android的主线程又叫UI线程,用于处理与UI相关的时间.Android仅仅同意在主线程中改动UI组件,当我们在其它线程中须要改动UI界面时.就须要发送消息到主线程 ...