ADO.Net的数据源和数据绑定控件

ASP.NET 2.0 引入了一系列可以改善数据访问的新工具,包括几个数据源和数据绑定控件。新增种类的数据源控件可以消除 ASP.NET 1.x 中要求的大量重复性代码。例如,您可以很容易地将 SQL 语句或存储过程与数据源控件相关联,并且将它们绑定到数据绑定控件。更令人感到印象深刻的是,通过 ObjectDataSource 控件可以简化开发和减少代码,并且仍然可以在 n 层体系结构的不同层中抽象业务和数据访问逻辑。

在 .NET 问世以前,用传统的 ASP 生成数据网格通常需要编写大量的代码,以便在遍历 ADO 记录集的同时即时生成 HTML 表。ASP.NET 1.x 通过允许您将基于 XML 的 DataSet 绑定到 ASP.NET DataGrid 控件,从而使该类型的开发变得更加容易。这就减少了生成网格所必需的代码。但是,传统的 ASP 和 ASP.NET 1.x 都要求代码实现分页、排序、编辑和行选择功能。通过 ASP.NET 2.0 中的改进功能,可以显著减少这些代码的数量,以产生带有完整分页、排序和编辑功能并填充了数据的网格。

在这一期的 Data Points 中,我将首先演示通过 ASP.NET 2.0 并使用 SqlDataSource 和一些新的数据绑定控件开发 Web 应用程序是多么容易。请注意,我在此使用的是 Beta 1 版本。

大多数企业应用程序都是在多层体系结构之上生成的,该体系结构有一个用于存放业务逻辑的中间层,以及一个使用一个或多个后端数据库的数据访问层。我将讨论 ObjectDataSource 可以多么理想地与现有的多层组件集成。通过将 ObjectDataSource 控件链接到业务对象,您可以充分利用现有的多层体系结构来生成完善的 Web UI,并且能够显著减少代码。 ObjectDataSource 控件还包含一些特殊的属性,使您可以绑定到 ASP.NET 2.0 和 ADO.NET 2.0 中新近增强的强类型 DataSet 和数据组件。 ASP.NET 2.0 中的其他新功能和改进包括新增的双向绑定表达式、增强的缓存,以及几个新增的可以数据绑定到新的数据源控件的 ASP.NET 2.0 控件。

数据绑定控件


要使用数据源控件,必须具有一个用来将它们绑定到的数据绑定控件。在 ASP.NET 2.0 中有几个新的数据绑定控件,包括 GridView、DetailsView 和 FormView 控件。如果您喜欢 ASP.NET 1.x DataGrid 控件,那么您也会喜欢 ASP.NET 2.0 GridView 控件。GridView 在本质上类似于 DataGrid,因为它可以绑定到新的数据源控件,并且可以用来实现排序、编辑和分页 — 它们需要的代码都比 DataGrid 少得多(有关 GridView 的详细信息,请参阅 Dino Esposito 在 MSDN®Magazine2004 年 8 月刊中发表的文章)。

要将 GridView 绑定到数据源控件,需要将 GridView 的 DataSourceID 属性设置为数据源控件的 ID。还可以设置 GridView 的其他几个属性来增强外观和用户交互(我将在稍后的示例中加以演示):

<asp:GridView ID="gvwOrders" Runat="server"
    DataSourceID="sdsOrdersDataSource" 
    AutoGenerateColumns="True">

其他控件(例如,DropDownList)也可以绑定到数据源控件。例如,DropDownList 控件可以绑定到检索雇员列表的 SqlDataSource 控件。雇员的全名可以显示在 DropDownList 中,而 EmployeeID 可以作为控件的基础数据值字段绑定到该控件。以下示例定义了一个 DropDownList,它将显示可供选择的客户名称的列表。客户数据被绑定到一个名为 sdsCustomerDataSource 的 SqlDataSource 控件,该控件可获得客户的 CompanyName 和 CustomerID 字段的列表:

<asp:DropDownList ID="ddlCustomers" Runat="server" AutoPostBack="True"
    DataSourceID="sdsCustomersDataSource"
    DataTextField="CompanyName" 
    DataValueField="CustomerID">
</asp:DropDownList>

在 ASP.NET 2.0 中,将控件绑定到数据源控件非常简单,并且不需要任何处于代码隐藏中的代码。但是,如果您愿意,仍然可以编写代码以显式绑定到控件。实际上,数据绑定控件的数据源和 DataMember 属性与 ASP.NET 1.x 相比已经得到了改进。

返回页首

数据源控件


在 ASP.NET 2.0 中有几个新的数据源控件,例如,SqlDataSource、ObjectDataSource、XmlDataSource、AccessDataSource 和 SiteMapDataSource(如图 1 所示)。它们全都可以用来从它们各自类型的数据源中检索数据,并且可以绑定到各种数据绑定控件。数据源控件减少了为检索和绑定数据甚至对数据进行排序、分页或编辑而需要编写的自定义代码的数量。

每个数据源控件都具有类似的属性,以便可以与其各自的数据源进行交互。生成 SiteMapDataSource 和 XmlDataSource 是为了检索分层数据,而生成其他数据源控件是为了检索带有列和行的基于集合的数据。

专门生成 AccessDataSource 以便从 Access 数据库中检索数据。SqlDataSource 听起来好像只能使用 SQL Server?,但实际情况不是这样的。它实际上可以用来从任何 OLE DB 或符合 ODBC 的数据源中检索数据。

命令类型和参数


SqlDataSource 控件具有四个命令属性,您可以设置这些属性以告诉 SqlDataSource 如何获得、插入、更新和删除它的数据。可以将 SelectCommand 属性设置为 SQL 语句或存储过程。在任何一种情况下,都可以根据需要传入参数。InsertCommand、UpdateCommand 和 DeleteCommand 属性用于告诉 SqlDataSource 使用哪些 SQL 语句(或存储过程)来修改基础数据库中的数据。图 2 中的代码示例显示了一个 SqlDataSource,它将它的 SelectCommand 和 UpdateCommand 属性设置为参数化的 SQL 语句。请注意,UpdateParameters 使用 Parameter 元素来指示要用于参数的字段的名称和数据类型。在将 GridView 绑定到该 SqlDataSource 以后,这些 UpdateParameters 值就被绑定到受影响行的具有相同名称的列。

您自己对此进行试验的最简单方式是,在 Visual Studio? 2005 中创建一个 Web 窗体,连接到服务器资源管理器窗口中的本地 SQL Server Northwind 数据库,然后将一个表拖到该 Web 窗体。这会自动创建一个 SqlDataSource 控件以及一个 GridView。Visual Studio 会自动将 SqlDataSource 控件的 ProviderName 和 ConnectionString 属性设置为 SQL Server Northwind 数据库。而且,所有四个命令属性都将被设置为适当的 SQL 语句。然后,您为编辑该 Web 窗体而必须完成的所有工作就是,使用智能标记来选中“Enable Editing”复选框(参见图 3)。

us0501DataPointsfig03

图 3 GridView设置


数据源控件还可以使用来自其他控件的参数。例如,数据源控件可以检索给定客户的所有订单。在这种情况下,CustomerID 可以是传递给 SqlDataSource 控件的 SelectCommand 属性的 SQL 语句或存储过程的参数。还可以从其他控件(例如,DropDownList)检索 CustomerID,并将其直接传递到 SqlDataSource 控件的 SQL SelectCommand 中。

您可以将控件的值直接链接到 SqlDataSource 控件的任一 SQL 语句(SelectCommand、InsertCommand、UpdateCommand 或 DeleteCommand)的参数,还可以指定究竟要将控件的哪个属性用于该参数。例如,如果您在上一个示例中不需要 DropDownList 的默认属性,而是需要它的 DataTextField,则可以将 ControlParameter 的 PropertyName 属性设置为 DataTextField。

除了 ControlParameter 以外,还可以将其他参数类型用于数据源控件。如果您要使用新的 ASP.NET 个性化功能,则可以使用 ProfileParameter 从配置文件对象中检索参数的值。接下来,还有几个从标准 Request 对象的集合中检索它们的数据的参数对象类型。例如,CookieParameter 可以用来从 Cookie 中检索参数的值。QueryStringParameter 从任意请求字符串变量中获得它的值,而 FormParameter 从 HTML 窗体的输入字段中获得它的值。最后,SessionParameter 可以用来从会话变量中检索它的值。这些类型的参数为数据源控件提供了多个有关如何设置它们的参数值的选项。

SqlDataSource 示例


既然我已经完成了概述,那么我将对使用 SqlDataSource 和 ObjectDataSource 来检索和修改数据进行一下对比。SqlDataSource 控件(它使用 ADO.NET 2.0 DbProviderFactory 对象)具有将它直接链接到 OLE DB 或 ODBC 数据源的属性。当加载包含链接到 SqlDataSource 的数据绑定控件的 ASP.NET 页时,SqlDataSource 直接与基础数据库进行通信。因而,SqlDataSourceData 源控件不与现有的业务对象集成。

为了查看 SqlDataSource 是如何操作的,让我们先观察一下 Orders_SDS.aspx 页(参见图 4)。有一个 DropDownList 控件,它绑定到一个从 Northwind 数据库中检索客户列表的 SqlDataSource 源控件。


图 4 通过 SqlDataSource 检索客户


还有另外一个名为 sdsOrdersDataSource 的 SqlDataSource,它用于检索选定客户的所有订单。图 5 中的代码(摘自 Orders_SDS.aspx — 它可在下载中得到)显示了两个 SqlDataSource 控件和这个 DropDownList。请注意,sdsOrdersDataSource SqlDataSource 控件使用 DropDownList 的选定值作为其存储过程的参数(prGet_Orders 的参数)。

ProviderName 和 ConnectionString 属性的组合告诉 SqlDataSource 控件从哪个数据存储中获得数据。尽管这些属性使 SqlDataSource 成为检索和修改数据的简单解决方案,但遗憾的是,它们在表示层的 ASPX 文件中公开了数据库连接字符串以及 SQL 语句或存储过程。这通常不是一个好主意。将该数据以加密形式存储在配置储存库(如配置文件或注册表)中要安全得多。

图 4 显示了编辑模式下的 Orders_SDS.aspx 页。分页由 GridView 自动实现(通过将 GridView AllowPaging 属性设置为 true,并将 PageSize 属性设置为期望的大小,如 10)。这将告诉网格将其中的行分页,并且在“下一页”或“上一页”链接被单击时自动重新加载网格和页。GridView 使用 TextBox 控件显示选定行的可编辑列。数据绑定列是通过 asp:BoundField 或 TemplateField 元素定义的。通过设置 DataField 属性,可以将 BoundField 绑定到 GridView 的关联数据源:

<asp:BoundField HeaderText="ShipCity" DataField="ShipCity" 
    SortExpression="ShipCity"></asp:BoundField>

这将告诉 GridView 控件,当它处于查看模式时,应该在 span 元素中显示 ShipCity 值。当 GridView 处于编辑模式时,选定行中的这一列将在适当的 HTML 元素中显示。在这种情况下,HTML 元素为 TextBox,因为它是一个字符串值。该元素是基于绑定列的数据类型选择的。例如,如果该列在 SQL Server 中被定义为位,则将使用 CheckBox 控件在编辑模式下显示该列。

TemplateField 元素在绑定列的行为方式方面提供了更大的灵活性。请观察以下摘自 Orders_SDS.aspx 页的代码示例:

<asp:TemplateField SortExpression="OrderDate" HeaderText="OrderDate">
    <ItemTemplate>
        <asp:Label ID="lblOrderDate_Item" Runat="server" 
            Text='<%# Bind("OrderDate", "{0:d}") %>'></asp:Label>
    </ItemTemplate>
    <EditItemTemplate>
        <asp:TextBox ID="txtOrderDate_Edit" Runat="server" 
            Text='<%# Bind("OrderDate", "{0:d}") %>'></asp:TextBox>
    </EditItemTemplate>
</asp:TemplateField>
 

当 GridView 处于查看模式时,它使用 TemplateColumn 在 Label 控件中显示 OrderDate 列;当 GridView 处于编辑模式时,它使用 TemplateColumn 在 TextBox 控件中显示 OrderDate 列。您还可以使用 FooterTemplate 来告诉 Template 列在页脚中以不同的方式显示该列。还可以使用 HeaderTemplate、AlternatingItemTemplate 甚至 InsertItemTemplate。还请注意 ASP.NET 2.0 中引入的简化的绑定语法。OrderDate 的值是通过调用 Bind 表达式并向它传递 GridView 的关联数据源中列的名称和一个可选的数据格式字符串表达式来设置的。在该示例中,我使用了表示短日期的数据格式字符串。这些属性都可以通过可借助于 Visual Studio 2005 中的新增智能标记功能访问的属性来轻松设置(参见图 6)。


图 6模板列属性


ObjectDataSource

GridView 和其他数据绑定控件的最出色的功能之一是,在设置了它们以后,只需更改单个属性,就可以将它们绑定到 ObjectDataSource 控件或 SqlDataSource 控件。例如,您必须完成的所有工作就是创建一个新的 ObjectDataSource 控件,并将 GridView 的 DataSourceID 属性更改为这个新的 ObjectDataSource 控件的 ID。

与 SqlDataSource 不同的是,ObjectDataSource 控件使您可以从 ASPX 页和表示层中抽象出特定于数据库的设置,并将它们移至多层体系结构中的较低层(参见图 7)。例如,SqlDataSource 控件的 ConnectionString、ProviderName 和 SelectCommand 属性在 ObjectDataSource 控件中不存在。相反,它们被替换为告诉 ObjectDataSource 控件实例化哪个业务类以及使用哪个方法来检索或修改数据的其他属性。


图 7


要设置 ObjectDataSource 控件以访问另一个层的业务类及其方法,必须首先将 ObjectDataSource 控件的 TypeName 属性设置为该业务类的名称(如 TypeName="MSDN2005Jan_BLL.Orders")。然后,将 SelectMethod 属性设置为该业务类中将用来检索数据源数据的方法的名称。该业务类的方法必须返回一个可枚举的列表,例如,集合、数组、DataSet 或 DataReader。为了让该方案能够工作,ObjectDataSource 必须能够执行指定的方法。如果它是静态方法,则不需要完成任何特殊的工作。如果该方法是实例方法,则 ObjectDataSource 必须能够创建该类的实例。要达到该目的,最简单的方法是将该业务类编写为包含默认的构造函数。另外,还可以处理 ObjectDataSource 的 ObjectCreating 事件,它使您可以用所需的任何构造函数来实例化该对象,然后将该对象实例传递给数据源控件。

图 8 中的代码示例(摘自同样包含在下载中的 Orders.aspx)显示了两个 ObjectDataSource 控件(它们替换了上一个示例中使用的两个 SqlDataSouce 控件)。odsOrdersDataSource 使用 MSDN2005Jan_BLL.Orders 类和它的 GetData 方法来检索它的订单列表。在该示例中,Orders 类中的 GetData 方法只是创建名为 OrdersDataSet 的强类型 DataSet 的实例及其相关的名为 OrdersTableAdapter 的适配器。然后,它调用 Fill 方法并返回强类型的 DataSet:

public OrdersDataSet GetData(string CustomerID)
{
    OrdersDataSet oDs = new OrdersDataSet();
    OrdersTableAdapter oDa = new OrdersTableAdapter();
    oDa.Fill(oDs, CustomerID);
    return oDs;
}

请注意,GetData 方法还接受由 ObjectDataSource 控件的 SelectParameter 属性传入的 CustomerID 参数。UpdateData 方法的参数也是从图 8中所示的 UpdateParameters 属性所指示的 ObjectDataSource 传入的。以下为 Orders 类的 UpdateData 方法的签名:

public void UpdateData(int OrderID, DateTime OrderDate, 
    string ShipCity, string ShipCountry)

UpdateData 方法的参数的名称和数据类型必须与 ObjectDataSource 控件中更新参数的名称和数据类型相匹配。数据源控件不能使用批处理更新,因此不能一次性地将多个行传递给更新方法。相反,必须将每个值作为单个参数传递给更新方法。除了 SelectMethod 和 UpdateMethod 属性以外,ObjectDataSource 控件还具有 DeleteMethod 和 InsertMethod 属性。

返回页首

增强的强类型DataSet


上一个示例演示了如何将 GridView 绑定到 ObjectDataSource 控件以链接到业务层的类,因此您可以通过该类的方法来检索和更新数据。如果您具有现有的业务层逻辑和多层体系结构,则该示例可以很好地工作。它还可以调用 Web 服务客户端代理的方法,或其他任何遵循类和方法要求的引用类的方法。

我故意将上个示例的一个方面延迟到现在才加以讨论,那就是强类型 DataSet。使用 Visual Studio 2005 中的向导,您还可以直接在类型化的 DataSet 类中定义方法,以便选择、插入、更新和删除数据。因而,您可以避免直接在业务层或数据访问层中编写任何 ADO.NET 代码,而是使用向导直接将 ADO.NET 逻辑添加到类型化的 DataSet 中。

您不必检索类型化的 DataSet,但是在这种情况下它是有价值的 — 这要归功于它的一些新的增强功能。类型化的 DataSet 创建了一个默认的 Fill 方法,该方法被追加到类型化 DataSet 的定义中的 TableAdapter 类。可以将这一可选的 TableAdapter 类设置为存储连接字符串以及存储过程或 SQL 语句,以便在数据库中选择、更新、插入和删除记录。


图 9 Orders DataSet


类型化的 DataSet 还允许您创建自定义的方法,以便检索和修改数据。在 Orders DataSet(如图 9 所示)中,我基于 prGet_Orders 存储过程创建了一个类型化的 DataSet。然后,通过数据组件查询配置向导,我向 OrdersTableAdapter 中添加了两个自定义方法:GetData 和 UpdateData。这些方法是在与类型化 DataSet 的 XSD 相关联的类文件内部定义的;在该示例中,我的文件名为 OrdersDataSet.Designer.cs。如果您想了解详细信息,可以打开这个自动生成的文件(但是您不应当修改它,因为如果该文件被重新生成,则您的更改将被改写)并查看自定义的 GetData 和 Update 方法以及为类型化 DataSet 创建的所有标准代码。如果您运行示例页 Orders2.aspx,则它会直接绑定到上述自定义方法。这可以显著减少您为中间层手动编写的代码数量。

其他数据源控件细节


现在我们已经完成了讨论,如果您知道数据源控件还可以通过一系列属性来公开缓存功能,则可能会很感兴趣。通过将 EnableCaching 设置为 true 并将 CacheDuration 设置为很多秒,数据将在缓存中存储相应的时间。还可以将 CacheExpirationPolicy 属性设置为 Absolute 或 Sliding。Absolute 是默认值,它告诉缓存在加载后立即开始倒计时直至过期。Sliding 策略告诉缓存在缓存数据每次被访问时重置过期倒计时。在您要加载的数据不是非常容易改变的情况下,缓存技术可以帮助优化应用程序。例如,在加载省、市甚至产品类别的 DropDownList 的数据源控件中使用缓存是有好处的,因为这些数据不会频繁更改。

ObjectDataSource 控件包装了挂钩到业务对象以调用业务方法的代码。它还与数据绑定控件(例如,GridView)协同工作,以执行分页、排序以及在 ASP.NET 1.x 中必须手动编码的数据更改。

返回页首

小结


ASP.NET 2.0 中的改进(尤其是在数据源和数据绑定控件领域中的改进)显著减少了产生带有完整分页、排序和编辑功能并填充了数据的网格所需的代码数量。尽管数据源控件消除了过去必须手动编写的大量代码,但您仍然可以编写代码以便与数据源控件进行交互。您不仅可以只通过指指点点来创建数据驱动的 Web 页,而且还可以编写代码来使用数据源控件的事件,例如,Selected、Selecting、Updated 或 Updating 事件。


dotnet 发表于:2006.11.02 11:05 ::分类: ( .NET技术新闻与资讯 ) ::阅读:(11次) :: 评论 (0)
===========================================================
使用 ASP.NET 2.0 ObjectDataSource 控件
===========================================================

简介

在 Microsoft ASP.NET 2.0 Framework 中,数据库访问得到了极大的简化。利用全新的 SqlDataSource 控件,您无需编写一行代码就可以选择、更新、插入和删除数据库数据。

生成简单的应用程序时,SqlDataSource 控件是一个很好的选择。如果您需要迅速生成一个使用户可以显示和编辑数据库记录的 Web 页,使用 SqlDataSource 控件在几分钟之内就能完成此工作。

例如,我自己就曾计时生成了这么一个页面。通过结合使用 SqlDataSource 控件与 GridView 控件,我在 1 分 15秒 内就能生成一个用于显示 Northwind Products 数据库表的内容的页面。就有这么快!

但是,SqlDataSource 控件存在一个问题。如果您使用 SqlDataSource 控件,那您就是在做不太妙的事情。SqlDataSource 控件的缺点在于它迫使您将用户界面层与业务逻辑层混合在一起。任何应用程序架构师都会告诉您:混合多个层的行为是不可取的。

生成严格意义上的多层 Web 应用程序时,您应该具有清晰的用户界面层、业务逻辑层和数据访问层。仅仅由于 SqlDataSource 控件的强制而在用户界面层引用 SQL 语句或存储过程是完全错误的。

那么为什么您要关心这些东西呢?不错,在很多情况下,您不必在意。如果您正在创建一个简单的 Web 应用程序,完全可以使用 SqlDataSource 控件。例如,如果您需要生成一个由单独页面组成的应用程序来显示数据库的表的内容,那么将应用程序划分为多个应用程序层就很不明智。

遗憾的是(如果您已经为此“交过学费”,则会感到幸运),并非所有的 Web 应用程序都很简单。应用程序达到一定的复杂程度之后,如果将其划分为多个应用程序层,则生成和维护它们就更轻松。

将应用程序划分为多个应用程序层有很多优点。如果您有一个清晰的业务逻辑层,就能够创建一个可以从多个页面调用的方法库。换句话说,创建一个清晰的业务逻辑层提升了代码重用。此外,创建清晰而独立的应用程序层使得应用程序更易于修改。例如,清晰的层次使您无需修改数据访问代码就可以修改用户界面。

如果您需要使用 ASP.NET Framework 生成多层 Web 应用程序,那么您可以使用 ASP.NET 2.0 Framework 所引入的另一个新控件:ObjectDataSource 控件ObjectDataSource 控件使您可将诸如 GridViewDropDownList 这样的用户界面控件绑定到一个中间层组件。

这篇文章的主题就是 ObjectDataSource 控件。在这篇文章中,您将学习如何使用此控件来显示和编辑数据库数据。我们还将讨论如何结合使用 ObjectDataSource 控件和 SqlDataSource 控件以简化数据库访问。

使用 ObjectDataSource 控件显示数据

我们在这里设想您需要创建一个用于显示 Products 数据库表的内容的 Web 页面。再进一步设想您的某个现有业务组件包含了一种用于检索此数据的方法。

例如,清单 1 中的组件包含了一个名为 GetProducts 的方法,此方法返回一个 DataReader 来表示 Products 数据库表的内容。

清单 1: ProductInfo.cs (C#)

using System;
using System.Data;
using System.Data.SqlClient;

public class ProductInfo
{
    const string conString = 
      "Server=localhost;Trusted_Connection=true;Database=Northwind";

    public static SqlDataReader GetProducts()
    {
        SqlConnection con = new SqlConnection(conString);
        string selectString = "SELECT * FROM Products";
        SqlCommand cmd = new SqlCommand(selectString, con);
        con.Open();
        SqlDataReader dtr = 
          cmd.ExecuteReader(CommandBehavior.CloseConnection);
        return dtr;
    }
}

清单 1: ProductInfo.vb (Visual Basic .NET)

Imports System.Data
Imports System.Data.SqlClient

Public Class ProductInfo
    Const conString As String = _
      "Server=localhost;Trusted_Connection=true;Database=Northwind"

    Public Function GetProducts() As SqlDataReader
        Dim con As New SqlConnection(conString)
        Dim selectString As String = "SELECT * FROM Products"
        Dim cmd As New SqlCommand(selectString, con)
        con.Open()
        Dim dtr As SqlDataReader =  _
          cmd.ExecuteReader(CommandBehavior.CloseConnection)
        Return dtr
    End Function

End Class

如果您将清单 1 中包含的这个类添加到应用程序的 Code 目录中,那么 ASP.NET Framework 将自动编译这个类。换句话说,只要向 Code 目录添加了这个类,就可以立即在 ASP.NET 页中使用它。

我们将使用 GridView 控件(在 ASP.NET 2.0 Framework 中替换了 DataGrid 控件)来显示由 GetProducts 方法返回的数据库记录。清单 2 中的 ASP.NET 页包含了一个绑定到 ObjectDataSource 控件的 GridView

清单 2: ShowProducts.aspx

<html>
<head>
    <title>Show Products</title>
</head>
<body>
    <form id="form1" runat="server">

    <asp:GridView
        ID="GridView1"
        DataSourceID="ObjectDataSource1"
        Runat="Server" />
        
     <asp:ObjectDataSource
        ID="ObjectDataSource1"
        TypeName="ProductInfo"
        SelectMethod="GetProducts"
        Runat="Server" />

    </form>
</body>
</html>

清单 2 中声明的 ObjectDataSource 控件包含两个重要的属性。TypeName 属性指示类名,而 SelectMethod 属性指示在选择数据时要在此类上调用的方法名。

在清单 2 中,ObjectDataSource 控件用于调用 ProductInfo 类上的 GetProducts 方法。由于 GridView 控件绑定到了 ObjectDataSource 控件上,因此通过 GridView 控件的 DataSourceID 属性,GridView 控件即可显示产品列表(请参见图 1)。


图 1. 使用 ObjectDataSource 控件显示产品

您可以结合使用 ObjectDataSource 控件与任何标准的 ASP.NET 数据绑定控件(例如,GridViewDropDownListTreeViewRepeater 控件)。ObjectDataSource 控件使您能够将任何标准控件绑定到组件。

SelectMethod 可以引用静态方法(在 Visual Basic .NET 中共享)或实例方法。如果您使用的是实例方法,则 ObjectDataSource 控件在调用这个方法前,会自动创建此组件的一个实例。在完成方法调用后,将自动销毁此组件。

结合使用参数与 ObjectDataSource 控件

您可以将参数与使用 ObjectDataSource 控件调用的方法一起使用。当您调用某方法时,如果需要将某些值(例如,控件属性或查询字符串的值)传递给此方法,则这种方式就非常有用。

在前一节中,我们使用 ObjectDataSource 控件创建了一个页面,用于显示来自 Products 数据库表的所有记录。在本节中,我们将修改此页面,以便允许用户从 DropDownList 控件(请参见图 2)选择产品类别。


图 2. 从 DropDownList 选择产品类别

清单 3 包含了修改后的 ProductInfo 组件。

清单 3: ProductInfo2.cs (C#)

using System;
using System.Data;
using System.Data.SqlClient;

public class ProductInfo2
{
    const string conString = 
      "Server=localhost;Trusted_Connection=true;Database=Northwind";

    public SqlDataReader GetProducts(string category)
    {
        SqlConnection con = new SqlConnection(conString);
        string selectString = "SELECT Products.* " +
          "FROM Products INNER JOIN Categories " +
          "ON Products.CategoryID=Categories.CategoryId " +
          "WHERE CategoryName=@CategoryName";
        SqlCommand cmd = new SqlCommand(selectString, con);
        cmd.Parameters.AddWithValue("@CategoryName", category);
        con.Open();
        SqlDataReader dtr = 
          cmd.ExecuteReader(CommandBehavior.CloseConnection);
        return dtr;
    }
}

清单 3: ProductInfo2.vb (Visual Basic .NET)

Imports System.Data
Imports System.Data.SqlClient

Public Class ProductInfo2

    Const conString As String = _
      "Server=localhost;Trusted_Connection=true;Database=Northwind"

    Public Function GetProducts(ByVal category As String) _
      As SqlDataReader
        Dim con As New SqlConnection(conString)
        Dim selectString As String = "SELECT Products.* " & 
          "FROM Products INNER JOIN Categories " & _
          "ON Products.CategoryID=Categories.CategoryId " & _
          "WHERE CategoryName=@CategoryName"
        Dim cmd As New SqlCommand(selectString, con)
        cmd.Parameters.AddWithValue("@CategoryName", category)
        con.Open()
        Dim dtr As SqlDataReader = _
          cmd.ExecuteReader(CommandBehavior.CloseConnection)
        Return dtr
    End Function

End Class

清单 3 中经过修改的 ProductInfo 组件包含了一个经过修改的 GetProducts 方法,此方法包含了一个用于类别名的参数。这个参数用于限制从数据库返回的产品。

清单 4 包含了 DropDownListGridViewObjectDataSource 控件,使您可以选择要显示的不同类别的产品。

清单 4: ShowProducts2.aspx

<html>
<head>
    <title>Show Products</title>
</head>
<body>
    <form id="form1" runat="server">

    <asp:DropDownList
        id="DropCategories"
        AutoPostBack="true"
        Runat="Server">
        <asp:ListItem Value="Beverages" />    
        <asp:ListItem Value="Seafood" />    
    </asp:DropDownList>
    
    <br /><br />
    
    <asp:GridView
        ID="GridView1"
        DataSourceID="ObjectDataSource1"
        Runat="Server" />
      
     <asp:ObjectDataSource
        ID="ObjectDataSource1"
        TypeName="ProductInfo2"
        SelectMethod="GetProducts"         
        Runat="Server">
        <SelectParameters>
            <asp:ControlParameter 
                Name="category"
                ControlID="DropCategories" />
        </SelectParameters>
     </asp:ObjectDataSource>

    </form>
</body>
</html>

从清单 4 的 DropDownList 控件选择新类别时,GridView 控件将自动地只显示来自选定类别的产品。

请注意,清单 4 中的 ObjectDataSource 控件包含了一个 SelectParameters 元素。这个元素列出了调用由 ObjectDataSource 控件的 SelectMethod 属性指定的方法时使用的所有参数。在本例中,SelectedParameters 元素包含了一个名为 category 的单个参数。这个参数表示来自 DropCategoriesDropDownList 控件的 SelectedValue 属性的值。

使用 ObjectDataSource 控件编辑数据

ObjectDataSource 控件包含 4 个重要属性:SelectMethod 属性、UpdateMethod 属性、InsertMethod 属性和 DeleteMethod 属性。综合利用这些属性,您能够指定执行标准数据库操作所需的所有方法。

例如,您可以使用 ObjectDataSource 控件来编辑数据库数据。在清单 5 中,修改后的 ProductInfo 类包含了一个新的 UpdateProductDeleteProduct 方法。

清单 5: ProductInfo3.cs (C#)

using System;
using System.Data;
using System.Data.SqlClient;

public class ProductInfo3
{
    const string conString = 
      "Server=localhost;Trusted_Connection=true;Database=Northwind";

    public static SqlDataReader GetProducts()
    {
        SqlConnection con = new SqlConnection(conString);
        string selectString = "SELECT ProductId,ProductName, " +
          "UnitPrice FROM Products ORDER BY ProductId";
        SqlCommand cmd = new SqlCommand(selectString, con);
        con.Open();
        SqlDataReader dtr = 
          cmd.ExecuteReader(CommandBehavior.CloseConnection);
        return dtr;
    }

    public static void UpdateProduct(int original_productId, 
      string productName, decimal unitPrice)
    {
        SqlConnection con = new SqlConnection(conString);
        string updateString = "UPDATE Products SET " + 
          "ProductName=@ProductName,UnitPrice=@UnitPrice " +
          "WHERE ProductID=@ProductID";
        SqlCommand cmd = new SqlCommand(updateString, con);
        cmd.Parameters.AddWithValue("@ProductName", productName);
        cmd.Parameters.AddWithValue("@UnitPrice", unitPrice);
        cmd.Parameters.AddWithValue("@ProductId", original_productId);
        con.Open();
        cmd.ExecuteNonQuery();
        con.Close();
    }

    public static void DeleteProduct(int original_productId)
    {
        SqlConnection con = new SqlConnection(conString);
        string deleteString = "DELETE Products " +
          "WHERE ProductID=@ProductID";
        SqlCommand cmd = new SqlCommand(deleteString, con);
        cmd.Parameters.AddWithValue("@ProductId", original_productId);
        con.Open();
        cmd.ExecuteNonQuery();
        con.Close();
    }
}

清单 5: ProductInfo3.vb (Visual Basic .NET)

Imports System.Data
Imports System.Data.SqlClient

Public Class ProductInfo3

    Const conString As String = _
      "Server=localhost;Trusted_Connection=true;Database=Northwind"

    Public Shared Function GetProducts() As SqlDataReader
        Dim con As New SqlConnection(conString)
        Dim selectString As String = "SELECT ProductId, " & _
          "ProductName,UnitPrice FROM Products ORDER BY ProductId"
        Dim cmd As New SqlCommand(selectString, con)
        con.Open()
        Dim dtr As SqlDataReader = _
          cmd.ExecuteReader(CommandBehavior.CloseConnection)
        Return dtr
    End Function

    Public Shared Sub UpdateProduct(ByVal original_productId _
      As Integer, ByVal productName As String, _
      ByVal unitPrice As Decimal)
        Dim con As New SqlConnection(conString)
        Dim updateString As String = "UPDATE Products " & _
          "SET ProductName=@ProductName,UnitPrice=@UnitPrice " & _
          "WHERE ProductID=@ProductID"
        Dim cmd As New SqlCommand(updateString, con)
        cmd.Parameters.AddWithValue("@ProductName", productName)
        cmd.Parameters.AddWithValue("@UnitPrice", unitPrice)
        cmd.Parameters.AddWithValue("@ProductId", original_productId)
        con.Open()
        cmd.ExecuteNonQuery()
        con.Close()
    End Sub

    Public Shared Sub DeleteProduct(ByVal original_productId _
      As Integer)
        Dim con As New SqlConnection(conString)
        Dim deleteString As String = "DELETE Products " & _
          "WHERE ProductID=@ProductID"
        Dim cmd As New SqlCommand(deleteString, con)
        cmd.Parameters.AddWithValue("@ProductId", original_productId)
        con.Open()
        cmd.ExecuteNonQuery()
        con.Close()
    End Sub

End Class      
      

您可以将这个修改过的 ProductInfo 类与清单 6 中包含的ObjectDataSource 控件一起使用,以编辑 Products 数据库表的内容。

清单 6: ShowProducts3.aspx

<html>
<head>
    <title>Show Products</title>
</head>
<body>
    <form id="form1" runat="server">

    <asp:GridView
        ID="GridView1"
        DataSourceID="ObjectDataSource1"
        DataKeyNames="ProductId"
        AutoGenerateColumns="false"
        AutoGenerateEditButton="true"
        AutoGenerateDeleteButton="true"
        Runat="Server">
        <Columns>
            <asp:BoundField 
                DataField="ProductName"/>
            <asp:BoundField
                DataField="UnitPrice" 
                DataFormatString="{0:c}"/>
        </Columns>
     </asp:GridView>   
        
     <asp:ObjectDataSource
        ID="ObjectDataSource1"
        TypeName="ProductInfo3"
        SelectMethod="GetProducts"
        UpdateMethod="UpdateProduct"        
        DeleteMethod="DeleteProduct" 
        Runat="Server">
        <UpdateParameters>
            <asp:Parameter 
                Name="original_productId" 
                Type="Int32" />
            <asp:Parameter 
                Name="productName" />
            <asp:Parameter 
                Name="unitPrice" 
                Type="Decimal"/>
        </UpdateParameters>
     </asp:ObjectDataSource>

    </form>
</body>
</html>

在清单 6 中,GridView 控件显示了ProductName 和 UnitPrice 这两个列的值。由于 GridView 控件的 AutoGenerateEditButtonAutoGenerateDeleteButton 属性均设置为值 True,GridView 将自动生成用于编辑和删除产品行的用户界面(请参见图 3)。


图 3. 使用 ObjectDataSource 控件编辑数据

当您单击 Update 链接更新产品时,ObjectDataSource 控件将调用 UpdateProduct 方法。请注意,ObjectDataSource 控件在其 UpdateParameters 元素中列出了传递给 UpdateProduct 方法的参数。

有一个参数需要额外进行讨论。我们需要将被更新的行的 ProductID 列的值传递给 UpdateProduct 方法由于没有在 GridView 中显示 ProductID 列,我们必须将 ProductID 列分配给 GridView 控件的 DataKeyNames 属性。这个列的名称变为 original_productId,而不是 productId,因为我们正在向 update 方法传递 ProductID 列的未编辑版本。

如果您单击 Delete 链接,ObjectDataSource 控件将调用 DeleteProduct 方法。由于 GridView 控件的 DataKeyNames 属性具有值 ProductId,因此会再次将一个名为 original_productId 的参数自动传递给 DeleteProduct 方法。

结合使用 ObjectDataSource 控件和 SqlDataSource 控件

到目前为止,我们已经将 ObjectDataSource 控件与使用 SqlDataReader 对象的组件一起使用,以便检索数据库数据。这里还有另一种选择。即不在组件内使用 SqlDataReaderDataSet 等 ADO.NET 对象,而是在组件中使用 SqlDataSource 控件。

您可以在组件中使用 SqlDataSource 控件的这一事实似乎很奇怪。通常,您不在组件中使用控件,因为控件一般具有可视化表示形式,而且控件参与页面执行生命周期。而 DataSource 控件在这一点上有些特殊。

如果您希望简化组件中的数据库访问代码,您可以在此组件中使用 SqlDataSource 控件。清单 7 展示了这种方式。

清单 7: ProductInfo4.cs (C#)

using System;
using System.Collections;
using System.Web.UI;
using System.Web.UI.WebControls;

public class ProductInfo4
{
    const string conString = 
      "Server=localhost;Trusted_Connection=true;Database=Northwind";

    public static IEnumerable GetProducts()
    {
        string selectString = "SELECT * FROM Products";
        SqlDataSource dsrc = new SqlDataSource(conString, 
          selectString);
        dsrc.DataSourceMode = SqlDataSourceMode.DataSet;
        return dsrc.Select(DataSourceSelectArguments.Empty);
    }
}

清单 7: ProductInfo4.vb (Visual Basic .NET)

Imports System.Collections
Imports System.Web.UI
Imports System.Web.UI.WebControls

Public Class ProductInfo4

    Const conString As String = _
      "Server=localhost;Trusted_Connection=true;Database=Northwind"

    Public Shared Function GetProducts() As IEnumerable
        Dim selectString As String = "SELECT * FROM Products"
        Dim dsrc As New SqlDataSource(conString, selectString)
        dsrc.DataSourceMode = SqlDataSourceMode.DataSet
        Return dsrc.Select(DataSourceSelectArguments.Empty)
    End Function

End Class

在清单 7 中,我们实例化了 SqlDataSource 控件的一个新实例。SqlDataSource 的构造函数接受了一个用于数据库连接字符串的参数和一个用于与 select 命令一起使用的命令文本的参数。接着,将 DataSourceMode 属性的值设为 DataSet(这里的另一个选项是 DataReader)。最后,在 SqlDataSource 控件的实例上调用 Select 方法,并返回表示来自 Products 数据库表的所有记录的 DataView

您可以将清单 7 中的 ProductInfo 类与清单 8 中包含的 ObjectDataSource 控件一起使用:

清单 8: ShowProducts4.aspx

<html>
<head>
    <title>Show Products</title>
</head>
<body>
    <form id="form1" runat="server">

    <asp:GridView
        ID="GridView1"
        DataSourceID="ObjectDataSource1"
        Runat="Server" />
        
     <asp:ObjectDataSource
        ID="ObjectDataSource1"
        TypeName="ProductInfo4"
        SelectMethod="GetProducts"         
        Runat="Server" />

    </form>
</body>
</html>

在清单 8 中,GridView 控件绑定到了 ObjectDataSource 控件上。而 ObjectDataSource 控件又调用了 ProductInfo4 类的 GetProducts 方法,以检索由 GridView 显示的数据。最后,GetProducts 方法使用 SqlDataSource 控件检索数据库数据。

小结

ASP.NET 2.0 Framework 极大地简化了数据库访问,使您可以更轻松地生成简单和复杂的 ASP.NET 应用程序。如果您需要生成一个简单的数据库驱动 Web 应用程序,那么您可以使用新的 SqlDataSource 控件。如果您需要生成一个更为复杂的应用程序或示例,您可以在传统的 3 层应用程序中使用 ObjectDataSource 控件。

ObjectDataSource 控件使您能够继续在数据驱动页面中使用中间层组件。其主要优势在于使您无需编写任何代码即可绑定到一个组件,从而极大简化了您的用户界面。使用 ObjectDataSource 控件,我可以在 3 分钟 20 秒的时间内生成一个可以显示 Product 数据库表的组件和页面。尽管这种方式花的时间比使用 SqlDataSource 控件长,但我感觉这样做时页面的体系结构要好得多。


dotnet 发表于:2006.11.02 10:59 ::分类: ( .NET技术新闻与资讯 ) ::阅读:(9次) :: 评论 (0)
===========================================================
ICollection 接口的类序列化的问题
===========================================================

先看一个现象:
如果你有这样的一个WebService 方法:

[WebMethod]
public void myTest(System.Collections.Specialized.NameValueCollection col)
{
.....
}

那你在请求这个WebService 方法的时候,会收到如下异常:

To be XML serializable, types which inherit from ICollection must have an implementation of Add(System.String) at all levels of their inheritance hierarchy. System.Collections.Specialized.NameValueCollection does not implement Add(System.String).

详细的错误发生点如下:
[InvalidOperationException: To be XML serializable, types which inherit from ICollection must have an implementation of Add(System.String) at all levels of their inheritance hierarchy.
System.Collections.Specialized.NameValueCollection does not implement Add(System.String).]
System.Xml.Serialization.TypeScope.GetDefaultIndexer(Type type, String memberInfo) +849
System.Xml.Serialization.TypeScope.ImportTypeDesc(Type type, MemberInfo memberInfo, Boolean directReference) +857
System.Xml.Serialization.TypeScope.GetTypeDesc(Type type, MemberInfo source, Boolean directReference, Boolean throwOnError) +135
System.Xml.Serialization.XmlReflectionImporter.ImportMemberMapping(XmlReflec tionMember xmlReflectionMember, String ns, XmlReflectionMember[] xmlReflectionMembers, Boolean rpc, Boolean openModel) +78
System.Xml.Serialization.XmlReflectionImporter.ImportMembersMapping(XmlRefle ctionMember[] xmlReflectionMembers, String ns, Boolean hasWrapperElement, Boolean rpc, Boolean openModel) +280


这是因为对 ICollection 接口的类进行序列化的一些特殊要求:
XmlSerializer 可以以不同方式处理实现 IEnumerable 或 ICollection 的类,条件是 这些类满足某些要求,如下所示。

1、实现 IEnumerable 的类必须实现带单个参数的公共 Add 方法。Add 方法的参数必须与 从 GetEnumerator 方法返回的 IEnumerator.Current 属性所返回的类型一致(多态) 。

2、除实现 IEnumerable 外还实现 ICollection 的类(如 CollectionBase)必须有一个 值为整数的公共 Item 索引属性(在 C# 中为索引器),并且必须有一个整数类型的公 共 Count 属性。传递给 Add 方法的参数必须与从 Item 属性返回的类型相同或与该类 型的某个基的类型相同。

3、对于实现 ICollection 的类,要序列化的值将从索引的 Item 属性检索,而不是通过 调用 GetEnumerator 来检索。另外,除返回另一个集合类(实现 ICollection 的集合 类)的公共字段之外,将不序列化其他公共字段和属性。

ICollection 接口的类要可以被序列化,该类必须包含 Add 方法和要序列化的 Item 属性(C# 索引器)。


上面的WebService 例子出现异常是因为:

NameValueCollection 并不直接实现 ICollection 接口。相反,NameValueCollection 扩展 NameObjectCollectionBase。这样,就会实现 ICollection 接口,并且在 NameValueCollection 类中不实现重载 Add(system.string) 方法。

在使用 XMLSerializer 时,XmlSerializer 尝试将 NameValueCollection 序列化或反序列化 为一般 ICollection。因此,它查找默认的 Add(System.String)。如果没有 Add(system.String) 方法,就会发生异常。

参考资料:

PRB:使用 XmlSerializer 序列化 NameValueCollection 对象时出 现“System.InvalidOperationException”错误
http://support.microsoft.com/default.aspx/kb/814187/zh-cn?spid=548&sid=304

序列化实现 ICollection 接口的类
http://msdn2.microsoft.com/zh-cn/library/58a18dwa.aspx

http://www.codecomments.com/archive321-2006-2-823224.html

http://www.experts-exchange.com/Programming/Programming_Languages/C_Sharp/Q_ 21585935.html
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值