ObjectDataSource类可将用户定义的类中方法的输出与数据绑定控件相关联。ObjectDataSource类对其所封装的类有一定的要求,并不是所有的类都能配合该数据源控件使用。具体来讲,可绑定类必须有默认的无参数构造函数,方法在语义上对应选择、更新、插入和删除操作。此外,该对象每次只能更新一项,不支持对其状态进行指更新。

ObjectDataSource的编程接口

  ObjectDataSource组件基本提供了SqlDataSource的编程接口,此外还添加了3个事件和几个属性。ObjectDataSource能引发的事件与其所绑定的底层业务对象的生存期有关--ObjectCreating、ObjectCreated、ObjectDisposing。下表列出了ObjectDataSource的关键属性:

  ObjectDataSource控件会通过反射对处理指定操作的方法进行定位和调用,TypeName属性会返回要调用的类的完全限定名。如果该类定义在App_Code目录下,我们不必指定程序集名称,否则,要使用这种形式的字符串:[类名,程序集名称]。

  注意:App_Code目录下放置过多的类在开发时会产生严重问题,因为任何文件的更改都将导致VS.NET重新编译项目中的所有文件。最好将业务逻辑对象封装在独立的程序集中,并使Web应用程序引用该程序集。

数据获取的实现

  下面的代码给出了一个能与ObjectDataSource配合的类。该类代表雇员,利用了两个辅助类:Employee和EmployeeCollection。

public class Employees
{
public static string ConnectionString
{
......
}
public static void Load(int employeeID)
{
......
}
public static void EmployeeCollection LoadAll()
{
......
}
public static EmployeeCollection LoadByCountry(string country)
{
......
}
public static void Save(Employee emp)
{
......
}
public static void Insert(Employee emp)
{
......
}
public static void Delete(int employeeID)
{
......
}
......
}

  如果不使用静态方法,配合ObjectDataSource的工作类必须拥有不带参数的默认构造函数。此外,这样的类不能维护任何状态。

  在aspx页面中使用该类:

<asp:ObjectDataSource runat="server" ID="MyObjectSource" TypeName="Core35.DAL.Employees"
   SelectMethod="LoadAll" />

   当HTTP运行库在Web页面中遇到类似的块时,它会在指定的类中生成调用LoadAll方法的代码。任何通过DataSourceID链接到MyObjectSource的控件都可绑定到返回的数据上。使用ObjectDataSource,只需编写工作类,而不必在页面的代码隐藏类中编写任何代码。

  示例代码:

<asp:DataGrid ID="grid" runat="server" DataSourceID="MyObjectSource" />

  与SelectMethod属性关联的方法返回的对象可以是IEnumerable对象、DataSet、DataTable或Object。虽然ObjectDataSource没有禁止在业务类中使用重载方法,但最好不要重载Select方法。在本例中也就是不要重载LoadAll方法。

参数的使用

  多数情况下,方法需要传入参数。我们可使用SelectParameters集合,将输入的参数添加到Select方法中。假设有一个根据国别加载雇员的方法,相关的ASP.NET标记代码如下:

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" TypeName="Core35.Dal.Employees"
SelectMethod
="LoadByCountry">
<SelectParameters>
<asp:ControlParameter Name="country" ControlID="Countries"
PropertyName
="SelectedValue" />
</SelectParameters>
</asp:ObjectDataSource>

  ControlParameter类会自动获取实现的参数值,并将其绑定到方法的参数列表中。如果要在该下拉列表中添加一下[All Countries]选项怎么办?如果选中该项,我们需要调用不带参数的LoadAll方法。如果选中具体国家,则调用带参数的LoadByCountry方法。在这种情况下,声明式编程就不能满足需求了,我们需要自己编写代码:

void Page_Load(object sender, EventArgs e)
{
//必须清空ObjectDataSource中的参数或禁用ViewState
ObjectDataSource.SelectParameters.Clear();
//选中[All Countries]时
if(Countries.SelectedIndex == 0)
ObjectDataSource1.SelectMethod
= "LoadAll";
else
{
ObjectDataSource1.SelectMethod
= "LoadByCountry";
ControlParameter cp
= new ControlParameter("country", "Countries", "SelectedValue");
ObjectDataSource1.SelectParameters.Add(cp);
}
}

  我们必须在页面加载时清除SelectParameters集合中的内容。该数据源控件(确切地说,是底层的视图控件)会将自身的大多数属性缓存到视图状态中。所以,若在更改列表选项后刷新页面,那么SelectParameters便是非空的。但出于性能的考虑,顺便关闭数据源控件的视图状态为上策。然而,如果关闭视图状态,被加载的数据源控件中所有集合都会为空。

数据与对象实例的缓存

  仅当指定的方法返回DataSet或DataTable对象时,ObjectDataSource组件才能对数据进行缓存。自定义对象的缓存必须自行实现。

  ObjectDataSource专门用于操作应用程序业务层的类,每次执行操作都会创建业务类的实例,操作结束后即被销毁。该模型是ASP.NET提倡的无状态编程模型的自然产物。如果业务对象初始化的代价较高,我们可借助静态类或实例类中的静态方法。

  业务对象的实例不会自动被缓存,但可以通过处理ObjectDataSource的ObjectCreating和ObjectDisposing事件来实现。ObjectCreating事件在ObjectDataSource控件需要业务类实现时引发,我们可编写相应的处理程序来获取该类现有实例,并将其返回给ObjectDataSource控件。

  示例代码:

public void BusinessObjectBeingCreated(object sender, ObjectDataSourceEventArgs e)
{
BusinessObject bo
= RetrieveBusinessFromPool();
if(bo == null)
bo
= new BusinessObject();
e.ObjectInstance
= bo;
}
  另一方面,在ObjectDisposing中,我们可存储该实例,并取消正在执行的释放操作:
public void BusinessObjectBeingDisposed(object sender, ObjectDataSourceDisposingEventArgs e)
{
ReturnBusinessObjectToPool(e.ObjectInstance);
e.Cancel
= true;
}

  这样,业务对象的Dispose方法不会被调用,也不会致使被缓存的对象处于已释放状态。

分页

  ObjectDataSource有三个与分页相关的属性:EnablePaging、StartRowIndexParameterName、MaximumRowsParameterName。

  ObjectDataSource提供了分页的基础架构,但实际的分页功能必须由绑定到ObjectDataSource的类来实现。下面的代码中,Customers类的一个LoadByCountry方法重载能接受两个额外的参数,分别用于指示单页的大小和当前页面中第一条记录的索引。这两个参数的名称必须分别赋给MaximumRowsParameterName和StartRowIndexParameterName。

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" TypeName="Core35.DAL.Customers"
EnableCaching="true"
StartRowIndexParameterName
="firstRow"
MaximumRowsParameterName
="totalRows"
SelectMethod
="LoadByCountry">
<SelectParameters>
<asp:ControlParameter Name="country" ControlID="Countries"
PropertyName
="SelectedValue" />
<asp:ControlParameter Name="totalRows" ControlID="PageSize"
PropertyName
="Text" />
<asp:ControlParameter Name="firstRow" ControlID="FirstRow"
PropertyName
="Text" />
</SelectParameters>
</asp:ObjectDataSource>
  分页功能的实现取决于方法的实现,必须手动编码。LoadByCountry有两个重载版本,其中一个版本支持分页:
public static CustomerCollection LoadByCountry(string country)
{
return LoadByCountry(country, -1, 0);
}
public static CustomerCollection LoadByCountry(string country, int totalRows, int firstRow)
{
CustomerCollection coll
= new CustomerCollection();
using(SqlConnection conn = new SqlConnection(ConnectionString))
{
SqlCommand cmd;
cmd
= new SqlCommand(CustomerCommands.cmdLoadByCountry, conn);
cmd.Parameters.AddWithValue(
"@country", country);
conn.Open();
SqlDataReader reader
= cmd.ExecuteReader();
HelperMethods.FillCustomerList(coll, reader, totalRows, firstRow);
reader.Close();
conn.Close();
}
return coll;
}

数据的更新与删除

  若要通过ObjectDataSource更新底层数据,我们需定义更新/插入/删除方法。这些操作通常需要参数,仅当参数的类型为简单类型时,才能使用命令参数集合。

<asp:ObjectDataSource ID="RowDataSource" runat="server"
TypeName
="Core35.DAL.Employees"
SelectMethod
="Load"
UpdateMethod
="Save"
DataObjectTypeName
="Core35.DAL.Employee">
<SelectParameters>
<asp:ControlParameter Name="id" ControlID="GridView1"
PropertyName
="SelectedValue" />
</SelectParameters>
<asp:ObjectDataSource>

  上例中通过Save方法保存记录,而该方法接受一个Employee对象。注意,如果设置DataObjectTypeName属性,UpdateParameters集合将被忽略。

  ObjectDataSource会在执行操作前实例化该对象的默认实例,然后试图用绑定控件中匹配的输入字段填充其公共成员。由于这项工作是通过反射完成的,因而绑定控件中输入字段的名称必须匹配于DataObjectTypeName所指代的对象暴露的公共属性。还要注意一个限制:不能像下面这样使用复杂数据类型定义Employee。

public class Employee
{
public string LastName{...}
public string FirstName{...}
...
public Address HomeAddress{...}
}

  只有少数ASP.NET控件(如GridView和DetailsView)能充分发挥数据源控件的功能。作为页面设计者,需要按照这种方法使输入字段匹配成员名称,即将GridView的列或DetailsView的DataField属性指向所要对应的数据源字段。数据字段的名称必须匹配更新/插入操作参数的公共属性名称(不区分大小写)。

  与插入操作不同,更新操作还需要主键来确定要更新的记录,如果使用显式的参数列表,添加代表ID的参数即可:

<asp:ObjectDataSource runat="server" ID="MyObjectSource"
TypeName
="Core35.SimpleBusinessObject"
SelectMethod
="GetEmployees"
UpdateMethod
="SetEmployee">
<UpdateParameters
>
<asp:Parameter Name="employeeid" Type="Int32" />
<asp:Parameter Name="firstname" Type="string" />
<asp:Parameter Name="lastname" Type="string" />
<asp:Parameter Name="country" Type="Int32" DefaultValue="null" />
</UpdateParameters>
</asp:ObjectDataSource>

  还有一种办法是设置主键,即通过GridView和DetailsView控件的DataKeyNames属性。设置该属性后,数据源控件会自动将某个参数添加到更新和删除命令的参数列表中。该参数的默认名称为orginal_XXX,其中XXX代表DataKeyNames的值。

  设置绑定控件的DataKeyNames的属性也是配置删除操作的简便方法。事实上,对于删除操作,我们不需指定其所有字段,设置主键足矣。

运行时的参数配置

  若ObjectDataSource配合的是ASP.NET专用控件(如GridView),那么大多数绑定操作都会自动完成,不需进行任何处理。但如果需要可通过Updating事件对更新过程进行控制:

protected void Updating(object sender, ObjectDataSourceMethodEventArgs e)
{
Employee emp
= (Employee)e.InputParameters[0];
emp.LastName
= "WhosThisGuy";
}

  该事件会在更新操作主体部分执行前引发。InputParameters集合中的参数会被传到更新方法中,该集合是只读的,不能添加或删除元素。然而,我们可以更改被传入的对象。

posted on 2011-04-19 13:48  辛勤的代码工  阅读(772)  评论(0编辑  收藏  举报