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

第一个,对整型变量进行非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. vue开发组件开发中的小技巧

    声明:以下随笔由博主自主编写,也有部分引用网友的,引用部分版权归原作者所有,其他博主原创部分禁止转载.复制全部或部分用以重新发布! vue递归组件事件阻止冒泡 其实这里主要还有递归组件的自定义事件不生 ...

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

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

  8. js开发中常用小技巧

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

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

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

随机推荐

  1. javascript 中的事件机制

    1.javascript中的事件. 事件流 javascript中的事件是以一种流的形式存在的. 一个事件会也有多个元素同时响应. 有时候这不是我们想要的效果, 我们只是需要某个特定的元素相应我们的绑 ...

  2. rdc21n(研发与设计综合讨论)博客开通了!

    rdc21n是“Research and Design Comprehensive discussioN”,其中21表示Comprehensive discussioN中间的21个字母(不包含空格), ...

  3. selenium 切换窗口 每次成功code

    最近用了网络上别人的一段切换窗口的code每次成功了,不错,学习 // 根据Title切换新窗口 public boolean switchToWindow_Title(WebDriver drive ...

  4. Java程序性能优化技巧

    Java程序性能优化技巧 多线程.集合.网络编程.内存优化.缓冲..spring.设计模式.软件工程.编程思想 1.生成对象时,合理分配空间和大小new ArrayList(100); 2.优化for ...

  5. 简单易懂的Activity四种启动模式

    Activity的四种启动模式 我们在项目开发的过程中,会涉及到应用中各个Activity的跳转,有些Activity是可以复用,不用重复加载,节约内存的使用. 将第二个Activity的启动模式修改 ...

  6. 地图、定位 CLLocationManager CLGeocoder CLPlacemark

    地图.定位 一.基本知识点 定位: 1.info.plist文件设置 ios8以后,使用定位需要在info.plist文件中添加两个字段NSLocationAlwaysUsageDescription ...

  7. NSThread 子线程 Cocoa NSOperation GCD(Grand Central Dispatch) 多线程

    单词:thread 英 θred:n 线.思路.vt 穿过.vi 穿透过 一.    进程.线程 进程:正在进行中的程序被称为进程,负责程序运行的内存分配,每一个进程都有自己独立的虚拟内存空间 线程: ...

  8. Scala.js v0.1 发布,在浏览器直接运行 Scala

    今天我们发布了 Scala.js 的首个版本,这个项目是在今年六月份的时候宣布的. 第一个版本支持的特性: 支持所有 Scala 特性,包括宏,不过有一些 语义上的区别 可非常好的跟 JavaScri ...

  9. SQL入门经典(七) 之脚本和批处理

    什么是脚本.我们前面学的CREATE TABLE <table name> ,USE <database name>这些都是脚本,为什么用脚本.脚本存储到文件中并且可以重复利用 ...

  10. ListView用法总结

    前言 列表,它作为一种非常重要的显示形式,不管是在web端还是在移动平台上,都是一种非常友好的,功能强大的展现形式.在Android中,ListView就接管了这一重任.尽管在Android5.X时代 ...