webabcd - 专注于asp.net

ASP.NET
从现在开始 一切都不晚
posts - 150, comments - 4182, trackbacks - 344, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理
[索引页]
[源码下载]


步步为营VS 2008 + .NET 3.5(11) - DLINQ(LINQ to SQL)之大数据量分页、延迟执行和日志记录


作者:webabcd


介绍
以Northwind为示例数据库,DLINQ(LINQ to SQL)之结合GridView控件和ObjectDataSource控件演示大数据量分页,同时介绍延迟执行和日志记录


示例
PagingAndLogging.aspx
<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="PagingAndLogging.aspx.cs"
    Inherits
="LINQ_DLINQ_PagingAndLogging" Title="大数据量分页、延迟执行和日志记录" 
%>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
    
<asp:GridView ID="gvProduct" runat="server" DataSourceID="odsProduct" AllowPaging="True" PageSize="5">
    
</asp:GridView>
    
<asp:ObjectDataSource ID="odsProduct" runat="server" EnablePaging="True" SelectCountMethod="GetProductCount"
        SelectMethod
="GetProduct" TypeName="PagingAndLogging">
        
<SelectParameters>
            
<asp:Parameter Name="startRowIndex" Type="Int32" DefaultValue="0" />
            
<asp:Parameter Name="maximumRows" Type="Int32" DefaultValue="10" />
        
</SelectParameters>
    
</asp:ObjectDataSource>
</asp:Content>

PagingAndLogging.cs
using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;

using System.ComponentModel;
using System.Collections.Generic;
using System.IO;
using DAL;

/// <summary>
/// PagingAndLogging 的摘要说明
/// </summary>

[DataObject]
public class PagingAndLogging
{
    [DataObjectMethod(DataObjectMethodType.Select, 
true)]
    
public List<Products> GetProduct(int startRowIndex, int maximumRows)
    
{
        NorthwindDataContext ctx 
= new NorthwindDataContext();

        
// System.Data.Linq.DataContext的记录日志的功能
        StreamWriter sw = new StreamWriter(HttpContext.Current.Request.PhysicalApplicationPath + "Log.txt"true);
        ctx.Log 
= sw;

        var products 
= (from p in ctx.Products
                        select p).Skip(startRowIndex).Take(maximumRows);

        
// products实现了IQueryable<T>接口
        
// 所以可以用如下方法从中获取DbCommand
        System.Data.Common.DbCommand cmd = ctx.GetCommand(products);
        
string commandText = cmd.CommandText;
        
foreach (System.Data.Common.DbParameter param in cmd.Parameters)
        
{
            
string parameterName = param.ParameterName;
            
object value = param.Value;
        }


        
// 延迟执行(Deferred Execution)
        
// products实现了IEnumerable<T>接口
        
// IEnumerable<T>接口的一个特性是,实现它的对象可以把实际的查询运算延迟到第一次对返回值进行迭代(yield)的时候
        
// ToList()之前,如果是LINQ to SQL的话,那么就可以通过products.ToString()查看LINQ生成的T-SQL
        
// ToList()后则执行运算
        var listProducts = products.ToList();

        
// 执行运算后System.Data.Linq.DataContext会记录日志,所以应该在执行运算后Close掉StreamWriter
        sw.Flush();
        sw.Close();

        
return listProducts;
    }


    
public int GetProductCount(int startRowIndex, int maximumRows)
    
{
        NorthwindDataContext ctx 
= new NorthwindDataContext();

        StreamWriter sw 
= new StreamWriter(HttpContext.Current.Request.PhysicalApplicationPath + "Log.txt"true);
        ctx.Log 
= sw;

        
// Count查询操作符(不延迟) - 返回集合中的元素个数
        int c = (from p in ctx.Products
                 select 
0).Count();

        sw.Flush();
        sw.Close();

        
return c;
    }

}


通过查看日志可以发现,单击第1页时DLINQ生成的T-SQL语句如下
SELECT TOP 5 [t0].[ProductID][t0].[ProductName][t0].[SupplierID][t0].[CategoryID][t0].[QuantityPerUnit][t0].[UnitPrice][t0].[UnitsInStock][t0].[UnitsOnOrder][t0].[ReorderLevel][t0].[Discontinued]
FROM [dbo].[Products] AS [t0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

SELECT COUNT(*AS [value]
FROM [dbo].[Products] AS [t0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

通过查看日志可以发现,单击第10页时DLINQ生成的T-SQL语句如下
SELECT TOP 5 [t1].[ProductID][t1].[ProductName][t1].[SupplierID][t1].[CategoryID][t1].[QuantityPerUnit][t1].[UnitPrice][t1].[UnitsInStock][t1].[UnitsOnOrder][t1].[ReorderLevel][t1].[Discontinued]
FROM (
    
SELECT ROW_NUMBER() OVER (ORDER BY [t0].[ProductID][t0].[ProductName][t0].[SupplierID][t0].[CategoryID][t0].[QuantityPerUnit][t0].[UnitPrice][t0].[UnitsInStock][t0].[UnitsOnOrder][t0].[ReorderLevel][t0].[Discontinued]AS [ROW_NUMBER][t0].[ProductID][t0].[ProductName][t0].[SupplierID][t0].[CategoryID][t0].[QuantityPerUnit][t0].[UnitPrice][t0].[UnitsInStock][t0].[UnitsOnOrder][t0].[ReorderLevel][t0].[Discontinued]
    
FROM [dbo].[Products] AS [t0]
    ) 
AS [t1]
WHERE [t1].[ROW_NUMBER] > @p0
-- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [45]
--
 Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

SELECT COUNT(*AS [value]
FROM [dbo].[Products] AS [t0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1


OK
[源码下载]

Feedback

#1楼    回复  引用  查看    

2007-10-23 08:29 by temptation      
好,学习

#2楼    回复  引用    

2007-10-23 08:57 by ivw [未注册用户]
支持。。。。
请问,用Linq跟原来那种操作SQL的方式比较有什么好跟不好的地方啊?

#3楼    回复  引用    

2007-10-23 09:08 by ivw [未注册用户]
你有Vs2008的下载地址吗?看来要装一个试试才行。

#4楼 [楼主]   回复  引用  查看    

2007-10-23 09:21 by webabcd      
@temptation
:)

@ivw
好处就是面向对象

vs2008 beta2 下载地址
http://www.microsoft.com/downloads/details.aspx?FamilyID=428c076f-e3ef-4290-9ff4-f6fd8f180b7d&DisplayLang=zh-cn

#5楼    回复  引用    

2007-10-23 09:38 by ivw [未注册用户]
晕,3+G,硬盘没那么多空间了。。:(

#6楼    回复  引用    

2007-10-23 09:44 by ivw [未注册用户]
能不能在javascript 里读取window.onresize里面的内容啊?window.onload的试过可以,但window.onresize好像不行。

#7楼 [楼主]   回复  引用  查看    

2007-10-23 11:52 by webabcd      
@ivw
:)
我下了很大的决心,把vs2005卸掉了

可以啊
<script>
window.onresize = function()
{
alert('webabcd');
}
</script>

#8楼    回复  引用    

2007-10-23 14:21 by ivw [未注册用户]
呵呵 ,那你05的项目怎么办?

我的意思不是这样啊,是判断如果原来有的话就把原来的读出来。再加上新的函数然后重新赋值。

#9楼    回复  引用    

2007-10-23 14:35 by ivw [未注册用户]
用RegisterHiddenField注册的控件在写控件的时候怎样要以得到他的值 啊?
如果把控件写在OnPreRender事件里会不会每次回发后都会把控件的值初始化?

#10楼    回复  引用    

2007-10-23 14:37 by ivw [未注册用户]
难道要用FindControl来查找刚定义的控件再取值?

#11楼 [楼主]   回复  引用  查看    

2007-10-23 15:16 by webabcd      
@ivw
现在的05的项目都在用08做

实现IPostBackDataHandler接口
在其LoadPostData方法中的NameValueCollection参数中取值

#12楼    回复  引用    

2007-10-23 15:23 by ivw [未注册用户]
那赋值呢?

#13楼    回复  引用    

2007-10-23 15:37 by ivw [未注册用户]
发个简单例子看看好吗

#14楼    回复  引用    

2007-10-23 17:22 by zz土 [未注册用户]
能不能胜任所有的SQL操作呀?如Case,Like,Charindex.....
不要学了又返回去学Sql

#15楼 [楼主]   回复  引用  查看    

2007-10-23 18:08 by webabcd      
@ivw
我没有例子,大概是这样的
类继承自IPostBackDataHandler

然后实现其方法
void IPostBackDataHandler.RaisePostDataChangedEvent()
{

}

bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection)
{
postCollection[key]; // 取值
return false;
}

赋值的话,你在RegisterHiddenField中就可以做的

#16楼 [楼主]   回复  引用  查看    

2007-10-23 18:10 by webabcd      
@zz土
大部分都没问题的,但肯定不是100%

sql是一定要学的

#17楼    回复  引用    

2007-10-23 18:22 by ivw [未注册用户]
key是不是指控件的ID?

#18楼    回复  引用    

2007-10-24 00:08 by 黑白 [未注册用户]
果然是方便

#19楼 [楼主]   回复  引用  查看    

2007-10-24 07:49 by webabcd      
@ivw
是的,就是在RegisterHiddenField时指定的ID

@黑白
:)
确实

#20楼    回复  引用    

2007-10-24 15:07 by ivw [未注册用户]
“赋值的话,你在RegisterHiddenField中就可以做的”

那如果我要在调用过程中重新赋值要怎么做啊?就像平时 的Text属性一样,那好像调用不了刚定义的隐藏控件啊。取值也是差不多。

#21楼 [楼主]   回复  引用  查看    

2007-10-25 08:03 by webabcd      
@ivw
RegisterHiddenField的时候你就要知道值了,因为RegisterHiddenField是要在PreRender中写的

#22楼    回复  引用    

2007-10-25 08:58 by ivw [未注册用户]
嗯,我用ViewState来保存值,但问题就是,如果在正常的情况下没有问题,但如果在Updatepanel里就不行了,是不是因为他所用的方式不一样啊?如果用
TextBox tb=new TextBox()Controls.add(tb);这种方法不能在扩展控件的时候创建出来啊

#23楼 [楼主]   回复  引用  查看    

2007-10-25 13:30 by webabcd      
@ivw
Updatepanel应该是支持ViewState的

可以的

#24楼    回复  引用    

2007-10-25 16:01 by ivw [未注册用户]
我在测试的时候不成功啊。。。:(....................
你的新帖子没有回复?

#25楼    回复  引用    

2007-10-25 23:27 by ivw [未注册用户]
Updatepanel是支持ViewState,但我说的是,如果在Updatepanel里LoadPostData下不能获取最新的值,只能获取上一次的值。

#26楼 [楼主]   回复  引用  查看    

2007-10-26 08:21 by webabcd      
@ivw
应该是可以的
你怎么写的?贴出来看看

#27楼    回复  引用    

2007-10-26 08:36 by ivw [未注册用户]
你可以在你扩展的GridView控件里看到这个问题,测试版的时候你是不是加了个滚动条记录的功能啊?如果把控件放到Updatepanel里这功能就会失效了。

#28楼 [楼主]   回复  引用  查看    

2007-10-26 13:24 by webabcd      
@ivw
那个测试版的问题比较多
你可以重写一个简单点的东西看看,是没问题的

#29楼    回复  引用    

2007-10-26 21:35 by ivw [未注册用户]
[DefaultProperty("Text"),ToolboxData("<{0}:LTextBox runat=server></{0}:LTextBox>")]
[ToolboxBitmap(typeof(LControls.LTextBox))]

#region 公共属性
public class LTextBox : TextBox, IPostBackDataHandler
{


[Bindable(true), Category("杂项"), DefaultValue(""), Localizable(true), Description("文本值。")]
public string Value
{
get
{
return ViewState["hidv"].ToString();
}
set
{ ViewState["hidv"] = value.Trim(); }
}


protected override void OnPreRender(EventArgs e)
{
this.Page.PreRenderComplete += new EventHandler(LTextBox_PreRender);
base.OnPreRender(e);
}


void LTextBox_PreRender(object sender, EventArgs e)
{
if (Page != null) Page.RegisterRequiresPostBack(this);
Page.ClientScript.RegisterHiddenField(this.ClientID + "_value", ViewState["hidv"] == null ? "" : ViewState["hidv"].ToString());

}

void IPostBackDataHandler.RaisePostDataChangedEvent()
{

}

bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection)
{

ViewState["hidv"] = postCollection["" + this.ClientID + "_value"];
return false;
}


控件基本是这样的,如果把生成的控件放到Updatepanel后,在页面里对Value属性赋值,再取值就可以重现问题了。

#30楼 [楼主]   回复  引用  查看    

2007-10-29 08:46 by webabcd      
@ivw
看了你的Value都是跟着ViewState走的
那是不能在客户端中赋值的

#31楼    回复  引用    

2007-10-29 11:18 by ivw [未注册用户]
兄弟这几天应该很忙吧,呵呵

但这样写不放在Updatepanel里就没问题啊,你有其它更好的方法吗?

#32楼 [楼主]   回复  引用  查看    

2007-10-29 11:43 by webabcd      
@ivw
:)
喝多了,头一直晕啊

突然想起来了,你在客户端给Value赋值,其实对应的就是TextBox控件的Text属性啊

#33楼    回复  引用    

2007-10-29 15:14 by ivw [未注册用户]
酒喝多了会伤身啊。呵呵 。:)

但如果我要Text跟Value独立出来啊,JS里只能认得出value=Text,新加的就不行了。

#34楼 [楼主]   回复  引用  查看    

2007-10-29 16:32 by webabcd      
@ivw
:)
是啊
那不如别做自定义控件了
可以换一种思路解决

#35楼    回复  引用    

2007-10-29 17:43 by ivw [未注册用户]
我想打算做的是用一个隐藏的控件来代替Value属性,读取值 都指向这个隐藏控件。

#36楼 [楼主]   回复  引用  查看    

2007-10-29 19:07 by webabcd      
@ivw
:)
那就在页面上多用一个隐藏控件不就行了吗

#37楼    回复  引用    

2007-10-29 20:35 by ivw [未注册用户]
但我想以后方便一些,不用每次都做重复的事,就想把这功能集成到TextBox里啊

#38楼 [楼主]   回复  引用  查看    

2007-10-30 07:51 by webabcd      
@ivw
要复用的话不如封装成一个用户控件,比较简单

#39楼    回复  引用    

2007-10-30 08:46 by ivw [未注册用户]
现在主要问题都是在新定义的属性跟隐藏控件之间的调用。哎。想了好久都没有什么好的办法。

#40楼 [楼主]   回复  引用  查看    

2007-10-30 11:39 by webabcd      
@ivw
:)
有的时候就要换一种思路才好

#41楼    回复  引用    

2007-10-30 12:32 by ivw [未注册用户]
那就麻烦兄弟有时间也帮忙想想,呵呵

#42楼 [楼主]   回复  引用  查看    

2007-10-30 13:07 by webabcd      
@ivw
:)
好的,原来你和我说过的,我一直是想做一个通用的,还没考虑得很清楚

#43楼    回复  引用    

2007-10-30 16:02 by ivw [未注册用户]
好像在继承控件的情况下不能用这种方法来添加实例的吧?
TextBox Tb=new TextBox()
Controls.Add(Tb)

#44楼 [楼主]   回复  引用  查看    

2007-10-31 08:24 by webabcd      
@ivw
是可以的

#45楼    回复  引用    

2007-10-31 08:33 by ivw [未注册用户]
我在上面那个TextBox的控件里用这种方法创建不了?

#46楼 [楼主]   回复  引用  查看    

2007-10-31 10:28 by webabcd      
@ivw
你的那个Controls是指什么?

#47楼    回复  引用    

2007-10-31 19:03 by ivw [未注册用户]
如果你要用add的方法添加控件,你会怎样做的啊?

#48楼 [楼主]   回复  引用  查看    

2007-11-01 08:33 by webabcd      
@ivw
和你一样啊
就是在某个容器上Add
不过其ViewState的保存是个问题

#49楼    回复  引用    

2007-11-01 21:41 by ivw [未注册用户]
但我用Add的方法不能为控件添加一个新的控件

#50楼 [楼主]   回复  引用  查看    

2007-11-02 08:30 by webabcd      
@ivw
可以的我在扩展GridView的分页上添加的LinkButton都是Add进去的

#51楼    回复  引用    

2007-11-02 13:00 by ivw [未注册用户]
你那里是先建一个TableCell,然后再在里面Add控件进去,但你是在OnRowCreated事件里做,会不会是这里有分别呢?

#52楼 [楼主]   回复  引用  查看    

2007-11-02 16:54 by webabcd      
@ivw
都是一样的
Add的控件肯定会出现到你的调用Add方法的那个容器里的

#53楼    回复  引用    

2007-11-02 22:07 by ivw [未注册用户]
那就奇怪了,我这样添加没成功。你有时间可以帮你试下吗》?

protected override void CreateChildControls()
{
TextBox tb = new TextBox();
tb.ID = "newtextbox";
Controls.Add(tb);
base.CreateChildControls();
}

#54楼    回复  引用    

2007-11-02 22:19 by ivw [未注册用户]
这样也试过
this.Controls.Add(tb);

#55楼 [楼主]   回复  引用  查看    

2007-11-05 08:39 by webabcd      
@ivw
Controls.Add(tb);

this.Controls.Add(tb);
是一样的
关键是看你的这个this能否添加控件

原来写过的继承自CompositeControl的控件,用你的方法是没问题的
那样this.Controls指的就是CompositeControl的Controls,然后在那里添加控件