【散300分】当你使用GridView的时候,不要在page_load中绑定数据

以专业开发人员为伍 2008-12-21 01:56:45
在csdn我看到的一个很普遍的编程错误,就是在使用GridView时在page_load中从后台(数据库)绑定数据。实际上,那种写法是asp.net1.1时代我们不太懂asp.net,并且使用DataGrid等比较简单的控件时遗留的一种非常不成熟的写法,没想到现在在很多使用GridView编程的“范例”代码中还不断地影响初学者。这个写法错误地在每一次回发时在page_load中绑定数据,而这个方法中实际上应该是创建上一个页面输出html时的GridView控件树结构,而并不是应该去根据当前后台数据库的最新数据去创建GridView的控件树结构。因此,在page_load中绑定数据不但是大大降低程序运行效率的(因为重复读取后台数据),而且还有逻辑错误。如果要手写绑定代码,那么所有在回发后的绑定功能也应该是在page_load结束之后的某个事件中,而不应该在page_load中。

虽然我使用数据源控件,无需手写相关代码,因此可以确保流程正确。但是,为了给不使用数据源控件绑定数据而是手写代码像GridView.DataSource属性传递数据的做法写一个Demo,我写了一个简化了的程序。(这里使用了.net2008的功能)


首先,在你的网站下创建一个cs文件来提供测试数据:
using System.Collections.Generic;
using System.Linq;

static public class BLL
{
private static List<TestType> ret;

static public IEnumerable<TestType> GetDatas(string cond1, string cond2)
{
if (ret == null)
{
ret = new List<TestType>();
for (int i = 0; i < 10; i++)
{
TestType x = new TestType
{
申请人名 = "王海波" + i.ToString(),
审核人名 = "",
审核意见 = ""
};
ret.Add(x);
}
}
var result = ret.Where(c => true);
if (cond1 != string.Empty)
result = result.Where(c => c.审核意见.Contains(cond1));
if (cond2 != string.Empty)
result = result.Where(c => c.审核人名 == cond2);
return result;
}

static public void UpdateRet(string name, string a, string b)
{
TestType line = ret.Where(x => x.申请人名 == name).FirstOrDefault();
if (line != null)
{
line.审核意见 = a;
line.审核人名 = b;
}
}

}

public class TestType
{
public string 申请人名 { get; set; }
public string 审核意见 { get; set; }
public string 审核人名 { get; set; }
}


这里,业务对象是由TestType来简单定义的。BLL类有两个方法,用来查询和更新数据。我使用一个 List<TestType> 类型的静态数据集合来保存演示测试数据。实际使用时则往往只要将方法改为对后台数据库操作即可,接口时一样的。


而一个处理界面的aspx内容如下:
<%@ Page Language="C#" %>

<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
this.GridView1.DataBind();
}

protected void GridView1_DataBinding(object sender, EventArgs e)
{
this.GridView1.DataSource = BLL.GetDatas(this.TextBox1.Text.Trim(), this.TextBox2.Text.Trim());
}

protected void GridView1_RowEditing(object sender, GridViewEditEventArgs e)
{
this.GridView1.EditIndex = e.NewEditIndex;
this.GridView1.DataBind();
}

protected void GridView1_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
{
this.GridView1.EditIndex = -1;
this.GridView1.DataBind();
}

protected void Button1_Click(object sender, EventArgs e)
{
this.GridView1.DataBind();
}

protected void 更新这一行(object sender, EventArgs e)
{
var bt = sender as LinkButton;
var a = (bt.FindControl("Label1") as Label).Text;
var b = (bt.FindControl("TextBox1") as TextBox).Text;
var c = (bt.FindControl("TextBox2") as TextBox).Text;
BLL.UpdateRet(a, b, c);
this.GridView1.EditIndex = -1;
this.GridView1.DataBind();
}

private string[] _审核意见 = null;

public string[] 审核意见
{
get
{
if (_审核意见 == null)
_审核意见 = new string[] { "", "同意", "不同意", "发回重审" };
return _审核意见;
}
}

protected void 选择审核意见(object sender, EventArgs e)
{
var list = sender as DropDownList;
var tb = list.FindControl("TextBox1") as TextBox;
tb.Text = list.SelectedValue;
}
</script>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
审核意见:<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<br />
审核人:<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<br />
<asp:Button ID="Button1" runat="server" Text="重新查询" OnClick="Button1_Click" />
<br />
<br />
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" OnDataBinding="GridView1_DataBinding"
OnRowCancelingEdit="GridView1_RowCancelingEdit" OnRowEditing="GridView1_RowEditing">
<Columns>
<asp:TemplateField HeaderText="申请人姓名" SortExpression="申请人名">
<EditItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Eval("申请人名") %>'></asp:Label>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Bind("申请人名") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="审核意见" SortExpression="审核意见">
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("审核意见") %>'></asp:TextBox>
<asp:DropDownList ID="DropDownList1" runat="server" DataSource="<%# this.审核意见 %>"
AutoPostBack="true" OnSelectedIndexChanged="选择审核意见">
</asp:DropDownList>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server" Text='<%# Bind("审核意见") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="审核人名" SortExpression="审核人名">
<EditItemTemplate>
<asp:TextBox ID="TextBox2" runat="server" Text='<%# Bind("审核人名") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label3" runat="server" Text='<%# Bind("审核人名") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField ShowHeader="False">
<EditItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="True" Text="更新"
OnClick="更新这一行"></asp:LinkButton>
 <asp:LinkButton ID="LinkButton2" runat="server" CausesValidation="False" CommandName="Cancel"
Text="取消"></asp:LinkButton>
</EditItemTemplate>
<ItemTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False" CommandName="Edit"
Text="编辑"></asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<br />
<asp:Button ID="Button2" runat="server" Text="什么也不做,专用来测试随机回发时页面状态是否正常" />
</form>
</body>
</html>


在界面中,有两个查询条件,第一个用于输入过滤审核意见(包含某些文字),第二个用来过滤审核人姓名。

请注意,数据时如何、合适绑定的。如果你在 BLL.GetDatas 方法设置一个断点,观察一下,何时才会读取后台数据库值。例如,你可以对某一行,点击“编辑”,然后再DropdownList上选择某个一个审批意见,然后再点击页面最底部的“什么也不做,专用来测试随机回发”按钮,你会看到DroopdownList中的值的状态并不会丢失,而且此时 BLL.GetDatas 中的断点也不会执行(说明并不读取后台数据库值),而且你在“审核意见”的get方法的第一行设置断点调试可以看到此时并不会重新读取DropdownList的后台数据(这说明每一行中某列嵌入复杂控件此时也不会重新绑定)。

而开头说的那种错误写法,则是大量地重复读取后台数据用于重新绑定,不但速度及其慢,而且逻辑上也是有bug的(只是你在开发用的机器上没有什么并发操作,很难发现bug)。

实际上,我看到很多在page_load中有为GridView的DataSource设置从后台查询的数据集来绑定控件做法,不但初学者会如此,写“范例”代码的blog中也会有很多。这是一个相当普遍的编程错误。
...全文
807 67 打赏 收藏 转发到动态 举报
写回复
用AI写文章
67 条回复
切换为时间正序
请发表友善的回复…
发表回复
iGreenHill 2010-08-26
  • 打赏
  • 举报
回复
up + mark

好东西,收藏
ln110119 2009-12-06
  • 打赏
  • 举报
回复
每天一小点的进步!UP。
skyworth98 2009-12-06
  • 打赏
  • 举报
回复
不懂,不搞web好多年了...
tan124 2009-12-06
  • 打赏
  • 举报
回复
这好的帖子我咋找没看见,害我每回都错,分没有不要紧,收藏
eternityzhu 2009-04-03
  • 打赏
  • 举报
回复
厉害啊。。。但我通常使用GridView的时候,是用objectdatasource来提供数据的,我根本就不知道GridView.bind的时候是在Page_Load中执行还是在Page_PreRender中执行
union59 2009-03-25
  • 打赏
  • 举报
回复
楼主对微软旧技术的精通和对微软新技术的专研之及时,之深刻,实在令人很汗,哈哈
liubaohuazy 2009-03-19
  • 打赏
  • 举报
回复
我也是这么写的。哈哈。领教了
如沐 2009-03-14
  • 打赏
  • 举报
回复
mark
veiny 2009-03-07
  • 打赏
  • 举报
回复
强烈关注中.................
Mi_Le 2009-03-05
  • 打赏
  • 举报
回复
wellcc 2009-03-02
  • 打赏
  • 举报
回复
到后面乱了
abcyzq 2009-02-18
  • 打赏
  • 举报
回复
请楼主出来解答哦。
MicroDeviser 2009-02-02
  • 打赏
  • 举报
回复
是啊,有点迷糊啊
wfyfngu 2009-01-23
  • 打赏
  • 举报
回复
一直怀疑SP1234大侠的表达能力
今天看来确实不咋地
楼主是不是想要说
“不应该把绑定GridView的代码放在Page_Load里的同时又不使用IsPostBack来限定”?
也就是说,楼主其实提倡下面的代码:

protected void Page_Load(object sender, EventArgs e) {
if(!IsPostBack) {
BindGrid();
}
}

private void BindGrid() {
gridview1.DataSource = GetData();
gridview1.DataBind();
}


而不是:

protected void Page_Load(object sender, EventArgs e) {
//if(!IsPostBack) {
BindGrid();
//}
}

private void BindGrid() {
gridview1.DataSource = GetData();
gridview1.DataBind();
}


至于楼主推荐的放到 Page_PreRender(object, EventArgs) 中,
我觉得更加让人迷惑。
cheniqi210 2009-01-17
  • 打赏
  • 举报
回复
看过LZ的一些帖子,感觉很有技术含量,人也很有才华.不过,这篇帖子怎么就这么垃圾呢?
如果是动态生成的控件,控件上动态绑定事件,你能搞个不在ISPOSTBACK判断外面绑定而成功的例子吗?

a13951845000 2009-01-13
  • 打赏
  • 举报
回复
mark
qq196260188 2009-01-09
  • 打赏
  • 举报
回复
up+mark
ojekleen 2009-01-08
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 sp1234 的回复:]
C# code
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
this.GridView1.DataBind();
}



这个写法其实是我们的一个懒习惯。按照逻辑,此时放在PreRender中其实是更加合适的,既:
C# code
protected void Page_PreRender(object sender, EventArgs e)
{
if (!IsPostBack)
this.GridView1.DataBind();
}



只是因为页面一创建,就…
[/Quote]

楼主比较的是不是这两方法?不过偶有几个疑惑啊,望楼主解答,放在 Page_PreRender的好处在哪?

在page_load中绑定数据不但是大大降低程序运行效率的(因为重复读取后台数据),而且还有逻辑错误。
page_load和Page_PreRender都会执行,如果不判断IsPostBack应该都是一样重复绑定数据的吧?


而开头说的那种错误写法,则是大量地重复读取后台数据用于重新绑定,不但速度及其慢,而且逻辑上也是有bug的(只是你在开发用的机器上没有什么并发操作,很难发现bug)。

并发操作的发就页面的CS文件是不可能发生的吧?每个请求都new一个新类,除非你的BLL层有可能造成并发错误,但是如果你的BLL层会有这种可能应该你的数据绑定在哪个事件中都会是一样造成并发错误的吧?

Page_PreRender是在控件事件后发生,page_load在控件事件前发生,试想一种可能啊,如果写this.bind()中,两种事件都没有加IsPostBack,而你在编辑GridView的时候是否出不了编辑效果?而在page_load事件却可以出效果,虽然这种效果很容易造你上述所说的错误。

在page_load还是在Page_PreRender事件中绑定数据哪个更好 能详细文字描述下?


ojekleen 2009-01-08
  • 打赏
  • 举报
回复
汗,不是吧,我怎么没太看明白?
xfei_liu 2009-01-01
  • 打赏
  • 举报
回复
太有用了,我得好好学学!谢谢!
加载更多回复(47)

62,046

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

试试用AI创作助手写篇文章吧