上一篇:打造通用ASP.NET数据分页控件(2) >>
打造通用ASP.NET数据分页控件(3)
为了提供一个健壮的、可伸缩的接插式体系结构,我们将利用[GoF] Builder模式构造出一个解决方案。
图四
IDataSourceAdapter接口定义了分页控件操作数据所需的最基本的元素,相当于“插头”。
publicinterface IDataSourceAdapter
{
int TotalCount{get;}
object GetPagedData(int start,int end);
}
TotalCount属性返回在处理数据之前数据源所包含元素的总数,而GetPagedData方法返回原始数据的一个子集,例如:假设数据源是一个包含20个元素的数组,分页控件将数据显示成每页10个元素,则第一页的元素子集是数组元素0-9,第二页的元素子集是数组元素10-19。DataViewAdapter提供了一个DataView类型的插头:
internal class DataViewAdapter:IDataSourceAdapter
{
private DataView _view;
internal DataViewAdapter(DataView view)
{
_view = view;
}
public int TotalCount
{
get{return (_view == null) ? 0 : _view.Table.Rows.Count;}
}
public object GetPagedData(int start, int end)
{
DataTable table = _view.Table.Clone();
for (int i = start;i<=end && i<= TotalCount;i++)
{
table.ImportRow(_view[i-1].Row);
}
return table;
}
}
DataViewAdapter实现了IDataSourceAdapter的GetPagedData方法,该GetPagedData克隆原始的DataTable,将原始DataTable中的数据导入到新的DataTable。该类的可见性有意地设置成internal,目的是为了向Web开发者隐藏实现细节,进而通过Builder类提供一个更简单的接口。
public abstract class AdapterBuilder
{
private object _source;
private void CheckForNull()
{
if (_source == null) throw new NullReferenceException("必须提供一个合法的数据源");
}
public virtual object Source
{
get
{
CheckForNull();
return _source;}
set
{
_source = value;
CheckForNull();
}
}
public abstract IDataSourceAdapter Adapter{get;}
}
AdapterBuilder抽象类为IdataSourceAdapter类型提供了一个更容易管理的接口,由于提高了抽象程度,我们不必再直接使用IdataSourceAdapter,同时AdapterBuilder还提供了在分页数据之前执行预处理的指令。另外,该Builder还使得实际的实现类,例如DataViewAdapter,对分页控件的用户透明:
public class DataTableAdapterBuilder:AdapterBuilder
{
private DataViewAdapter _adapter;
private DataViewAdapter ViewAdapter
{
get
{
if (_adapter == null)
{
DataTable table = (DataTable)Source;
_adapter = new DataViewAdapter(table.DefaultView);
}
return _adapter;
}
}
public override IDataSourceAdapter Adapter
{
get
{
return ViewAdapter;
}
}
}
public class DataViewAdapterBuilder:AdapterBuilder
{
private DataViewAdapter _adapter;
private DataViewAdapter ViewAdapter
{
get
{ // 延迟实例化
if (_adapter == null)
{
_adapter = new DataViewAdapter((DataView)Source);
}
return _adapter;
}
}
public override IDataSourceAdapter Adapter
{
get{return ViewAdapter;}
}
}
DataView类型和DataTable类型的关系是如此密切,所以构造一个通用性的DataAdapter可能是有意义的,其实只要加入另一个处理DataTable的构造函数就足够了。遗憾的是,当用户需要不同的功能来处理某个DataTable时,就必须替换或继承整个类。如果我们构造一个使用同一IdataSourceAdapter的新Builder,用户在选择如何实现适配器时就拥有更多的自由。
在分页控件中,寻找适当Builder类的操作由一个类型安全的集合完成。
public class AdapterCollection:DictionaryBase
{
private string GetKey(Type key)
{
return key.FullName;
}
public AdapterCollection() {}
publicvoid Add(Type key,AdapterBuilder value)
{
Dictionary.Add(GetKey(key),value);
}
publicbool Contains(Type key)
{
return Dictionary.Contains(GetKey(key));
}
publicvoid Remove(Type key)
{
Dictionary.Remove(GetKey(key));
}
public AdapterBuilder this[Type key]
{
get{return (AdapterBuilder)Dictionary[GetKey(key)];}
set{Dictionary[GetKey(key)]=value;}
}
}
AdapterCollection依赖于DataSource类型,DataSource通过BoundControl_DataBound巧妙地引入。这里使用的索引键是Type.FullName方法,确保了每一种类型索引键的唯一性,同时这也把保证每一种类型只有一个Builder的责任赋予了AdapterCollection。将Builder查找加入BoundControl_DataBound方法,结果如下:
public AdapterCollection Adapters
{
get{return _adapters;}
}
private bool HASParentControlCalledDataBinding
{
get{return _builder != null;}
}
private void BoundControl_DataBound(object sender,System.EventArgs e)
{
if (HasParentControlCalledDataBinding) return;
Type type = sender.GetType();
_datasource = type.GetProperty("DataSource");
if (_datasource == null)
throw new NotSupportedException("分页控件要求表现控件必需包含一个DataSource。");
object data = _datasource.GetGetMethod().Invoke(sender,null);
_builder = Adapters[data.GetType()];
if (_builder == null)
throw new NullReferenceException("没有安装适当的适配器来处理下面的数据源类型:"+data.GetType());
_builder.Source = data;
ApplyDataSensitivityRules();
BindParent();
RaiseEvent(DataUpdate,this);
}
BoundControl_DataBound方法利用HasParentControlCalledDataBinding检查是否已经创建了Builder,如果是,则不再执行寻找适当Builder的操作。Adapters表的初始化在构造函数中完成:
public Pager()
{
SelectedPager=new System.Web.UI.WebControls.Style();
UnselectedPager = new System.Web.UI.WebControls.Style();
_adapters = new AdapterCollection();
_adapters.Add(typeof(DataTable),new DataTableAdapterBuilder());
_adapters.Add(typeof(DataView),new DataViewAdapterBuilder());
}
最后一个要实现的方法是BindParent,用来处理和返回数据。
private void BindParent()
{
_datasource.GetSetMethod().Invoke(BoundControl,
new object[]{_builder.Adapter.GetPagedData(StartRow,ResultsToShow*CurrentPage)});
}
这个方法很简单,因为数据处理实际上是由Adapter完成的。这一过程结束后,我们还要用一次Reflection API,不过这一次是设置表现控件的DataSource属性。
下一篇:打造通用ASP.NET数据分页控件(4) >>
相关文章:
- · 在ASP.NET中实现会话状态基础(3)
- · 在ASP.NET中实现会话状态基础(4)
- · Asp.net入门之吸星大法
- · 在ASP.NET 2.0中实现本地化(1)
- · 在ASP.NET 2.0中实现本地化(2)
- · WebMatrix开发ASP.NET试用手记(1)
- · WebMatrix开发ASP.NET试用手记(2)
- · WebMatrix开发ASP.NET试用手记(3)
- · 表单填写时用回车代替TAB的实现方式
- · ASP使用MYSQL数据库全攻略
- · 友情连接浏览器
- · 方便购买的电子商务站点设计技巧
- · 使用ActiveX控件开发网页常见的问题
- · 基于ACCESS数据库的纯asp论坛制作心得
- · 关于打印页面的一些经验
- · vbscript和javascript互相调用
- · 使用索引服务器 - 增加属性
- · 使用索引服务器 - 创建ASP页面
- · 使用索引服务器- 使用索引服务器的对象
- · 已调试好的asp程序在VB中转换为组件的技巧
- · ASP.NET HTTP运行时组成详解(1)
- · 实现类似Windows资源管理器的DataGrid(2)
- · ASP.NET HTTP运行时组成详解(2)
- · ASP.NET HTTP运行时组成详解(3)
- · ASP.NET HTTP运行时组成详解(4)
- · ASP.NET HTTP运行时组成详解(5)
- · ASP.NET中使用TreeView控件(1)
- · ASP.NET中使用TreeView控件(3)
- · ASP.NET中使用TreeView控件(2)
- · ASP.NET中使用TreeView控件(4)
- · ASP.NET服务器控件发送脚本(1)
- · ASP.NET服务器控件发送脚本(2)
- · ASP.NET服务器控件发送脚本(3)
- · ASP.NET服务器控件发送脚本(4)
- · ASP.NET+XML打造网络硬盘(1)
- · ASP.NET+XML打造网络硬盘 (3)
- · ASP导出Excel数据的四种方法(2)
- · ASP.NET应用程序设计的10大技巧(1)
