ASP.NET2.0 ObjectDataSource的使用详解(2)

4 绑定到业务逻辑

在上面GetProduct的定义时,可以看到该方法返回的类型是SqlDataReader,由于ObjectDataSource将来需要作为绑定控件的数据来源,所以它的返回类型必须如下的返回类型之一:

IenumerableDataTableDataViewDataSet或者Object

为了更好的进行业务处理,我们需要更进一步的封装业务逻辑,以便返回强类型。接下来我们定义一个Product类来封装数据库,具体由Product.cs实现并放置在App_Code目录,代码如下

using System;

public class Product

{

    protected int _productID;

    protected String _productName;

    protected int _categoryID;

    protected decimal _price;

    protected int _inStore;

    protected String _description;

    public int ProductID

    {

        get { return _productID; }

        set { _productID = value; }

   

    }

    public String ProductName

    {

        get { return _productName; }

        set { _productName = value; }

    }

    public int CategoryID

    {

        get { return _categoryID; }

        set { _categoryID = value; }

    }

    public decimal Price

    {

        get { return _price; }

        set { _price = value; }

    }

    public int InStore

    {

        get { return _inStore; }

        set { _inStore = value; }

    }

    public String Description

    {

        get { return _description; }

        set { _description = value; }

    }

    public Product()

    { }

    public Product(int productID, string productName, int categoryID, decimal price, int instore, string description)

    {

        this._productID = productID;

        this._productName = productName;

        this._categoryID = categoryID;

        this._price = price;

        this._inStore = instore;

        this._description = description;

    }

}

         代码2-12 Product.cs源代码



然后在业务处理里定义
ProductDB.cs类来进行处理,代码如下

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Configuration;

using System.Data;

using System.Data.Common;

using System.Data.SqlClient;

using System.Web;

/// <summary>

/// Summary description for ProductDB

/// </summary>

public class ProductDB

{

   public ProductDB()

   {}

    public List<Product> LoadAllProduct()

    {

        List<Product> products = new List<Product>();

        SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);

        string commandText = "select * from Products";

        SqlCommand command = new SqlCommand(commandText, conn);

        conn.Open();

        SqlDataReader dr = command.ExecuteReader();

        while (dr.Read())

        {

            Product prod = new Product();

            prod.ProductID = (int)dr["ProductID"];

            prod.ProductName = (string)dr["ProductName"];

            prod.CategoryID = (int)dr["CategoryID"];

            prod.Price = (decimal)dr["price"];

            prod.InStore = (Int16)dr["InStore"];

            prod.Description = (String)dr["Description"];

            products.Add(prod);

        }

        dr.Close();

        conn.Close();

      return products;

    }

    public void UpdateProduct(Product pro)

    {

        SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);

        SqlCommand updatecmd = new SqlCommand("UPDATE Products set ProductName=@ProductName,CategoryID=@CategoryID,Price=@Price,InStore=@InStore,Description=@Description where ProductID=@ProductID", conn);

        updatecmd.Parameters.Add(new SqlParameter("@ProductName", pro.ProductName));

        updatecmd.Parameters.Add(new SqlParameter("CategoryID", pro.CategoryID));

        updatecmd.Parameters.Add(new SqlParameter("@Price", pro.Price));

        updatecmd.Parameters.Add(new SqlParameter("@InStore", pro.InStore));

        updatecmd.Parameters.Add(new SqlParameter("@Description", pro.Description));

        updatecmd.Parameters.Add(new SqlParameter("@ProductID",pro.ProductID));

        conn.Open();

        updatecmd.ExecuteNonQuery();

        conn.Close();

    }

    public void DeleteProduct(Product pro)

    {

        SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);

        SqlCommand delcmd = new SqlCommand("delete from Products where ProductID=@ProductID", conn);

        delcmd.Parameters.Add(new SqlParameter("@ProductID", pro.ProductID));

        conn.Open();

        delcmd.ExecuteNonQuery();

        conn.Close();

    }

}

     代码2-13 ProductDB.cs源代码

在这段代码里使用了泛型,所以需要导入System.Collections.Generic;命名空间,编辑和删除传递的参数是Product类型。另外在Command命令参数的加入方式上使用的是Add,读者也可以想上面一样使用AddWithValue方法。

 然后建立DB_ObjectDataSource.aspx页面,下面列出了ObjectDataSource设置如2-14

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="LoadAllProduct"

              DataObjectTypeName="Product" TypeName="ProductDB" DeleteMethod="DeleteProduct" UpdateMethod="UpdateProduct"></asp:ObjectDataSource>

    代码2-14 DB_ObjectDataSource.aspx部分代码

在这段代码里使用了DataObjectTypeName属性,并将改值设置为Product类。运行结果和图3-34一样。

5 DataKeyNamesOldValuesParameterFormatString

1DataKeyNames

如果读者使用Simple_ObjectDataSource.aspx,可以发现如果没有设置GridViewDataKeynames属性,则无法更新或者删除操作。

Update Delete 操作中扮演特殊角色的一个重要属性是 DataKeyNames 属性。此属性通常被设置为数据源中字段的名称,这些字段是用于匹配该数据源中的给定行的主键的一部分。当以声明方式指定此属性时,多个键之间用逗号分隔,尽管通常情况下只有一个主键字段。

为了保留原始值以传递给 Update Delete 操作,DataKeyNames 属性指定的字段的值在视图状态中往返,即使该字段并未作为 GridView 控件中的列之一被呈现。当 GridView 调用数据源 Update Delete 操作时,它在一个特殊的 Keys 字典中将这些字段的值传递给数据源,该字典独立于包含用户在行处于编辑模式时(对于更新操作)输入的新值的 Values 字典。 Values 字典的内容是从为处于编辑模式的行呈现的输入控件中获得的。

例如,假设数据库里由如下一条记录

ProductID     ProductName   CategoryID     Price         InStore     Description

24             生物技术          7             9.0000         2          生物技术丛书

为了进行数据传递,在实际执行该行的编辑时,ASP.NET框架将以Key/Value字典的形式进行存储的,这里我们将它写成如下的方式以便理解:

key                     value

{"@ProductID"   ,           "24"     }

{"@ProductName",          "生物技术"}

{"@CategoryID",            "7"      }

{"@price",                   "9.0000"}

{"@InStore",                "2"      }

{"@Description",             "生物技术丛书"}

然而我们知道在数据库里productID是递增的主键,所以在实际更新中,该行并不需要进行更新,若要排除此字典中的该字段,我们可以在GridView的绑定列中设置该列为只读。当将 Columns 集合中的对应 BoundField ReadOnly 属性设置为 true时,该字段将不会在key/value里传递。另一方面还请注意,默认的如果在 Visual Studio 中使用 GridView 设计器,主键字段的 ReadOnly 属性会自动设置为 true

由于我们在前面的演示里已经将ProductID设置为已经将ProductID设置为ReadOnlytrue,自然的传递到UpdateProductKey/Value的值为:

{"@ProductName",          "生物技术"}

{"@CategoryID",            "7"      }

{"@price",                   "9.0000"}

{"@InStore",                "2"      }

{"@Description",             "生物技术丛书"}

这里可以看到没有了@ProductID列,那么系统如何知道你当前编辑的ProductID呢?这个功能就是由DataKeyNames来完成。原来当您将GridViewDataKeyNames设置为ProductID时,该列在更新时会自动调用ProductID的数值。

在删除方法DeleteProduct里,如果您设置断点查看pro的值,您会发现在删除产品里仅仅传递DataKeyname的值(也就是仅仅传递ProductID),而并不传递ProductNameCategoryID等的值,所以您会发现,对于编辑我定义的方式是:

public Product(int productID, string productName, int categoryID, decimal price, int instore, string description)

对于删除,我定义的方式是

public   void DeleteProduct(int ProductId)

就是这个原因。

2OldValuesParameterFormatString

在使用前面的例子里,请注意分配给 UpdateCommand Update 语句中的参数的命名约定。UpdateDelete的参数都采用默认的列命名方式,例如ProductDAL.cs里的DeleteProduct定义如下:

public   void DeleteProduct(int ProductId)

        {

            SqlConnection con = new SqlConnection(_connectionString);

            string deleteString = "DELETE FROM Products WHERE ProductID=@ProductID";

            SqlCommand cmd = new SqlCommand(deleteString, con);

            cmd.Parameters.AddWithValue("@ProductID", ProductId);

            con.Open();

            cmd.ExecuteNonQuery();

            con.Close();

        }

 如果你想更改列的名称,例如更改DeleteProduct如下

public   void DeleteProduct(int old_ProductId)

       {

           SqlConnection con = new SqlConnection(_connectionString);

           string deleteString = "DELETE FROM Products WHERE ProductID=@ProductID";

           SqlCommand cmd = new SqlCommand(deleteString, con);

           cmd.Parameters.AddWithValue("@ProductID", old_ProductId);

           con.Open();

           cmd.ExecuteNonQuery();

           con.Close();

       }

 那么你在运行时将出现错误如图2-34



ASP.NET2.0 ObjectDataSource的使用详解(2)
                
2-34 参数不匹配错误

 

这是因为GridView 和其他数据绑定控件调用 Update 操作的自动功能需依赖此命名约定才能工作。参数的命名预期应与 SelectCommand 返回的关联字段值相同。使用此命名约定使得数据绑定控件传递给数据源的值与 SQL Update 语句中的参数相对应成为可能。

此默认命名约定的使用假设 Keys Values 字典的内容相互排斥 -- 即用户能够在数据绑定控件处于编辑模式时更新的字段值的命名应该与用于匹配要更新的行的字段值(对于 SqlDataSource,这些字段值在 WHERE 子句中)的命名不同。考虑这点的另一种方式是在 DataKeyNames 上设置的任何字段都应该设置为只读或在数据绑定控件中(例如在 GridView Columns 集合中)不可见。虽然键字段为只读的情况很普遍,但是存在一些有效的方案,其中您将希望能够更新同时还用于匹配要更新的数据行的字段。

例如,如果我们将Products数据库的ProductID列在设计表格结构时设置为nvarchar,它存放的是图书ISDN编号,该编号并不是递增的,因此在运行时,您可以更改ProductID的只,前提是主要不重复即可。

这样我们就需要将该ProductID列设置为ReadOnly=”false”以便允许编辑,另一方面,为了确认哪条记录被更新还需要传递该列的只到更新/删除方法,所以还需要将DataKeyNames设置为ProductID

这样GridView 将在 Keys 字典中传递该字段的旧值,而在 Values 字典中传递该字段的新值。仍以UpdateProduct为例,当将ProductIDReadOnly设置为”false”,并且将DataKeyNames设置为ProductID后,对于前面介绍的这条记录

ProductID     ProductName   CategoryID     Price         InStore     Description

24             生物技术          7             9.0000         2          生物技术丛书

我想将ProductID更改为ISBN001,此时传递给UpdateProduct的方法是:

key                     value

{"24 "   ,           "ISBN001"     }

{"@ProductName",          "生物技术"}

{"@CategoryID",            "7"      }

{"@price",                   "9.0000"}

{"@InStore",                "2"      }

{"@Description",             "生物技术丛书"}

我们之所以需要这两个值是因为,利用“24”我们需要获取该记录,利用“ISBN001”我们需要知道将来要把24更新为什么值。因为这样我们就需要区分Keyvalue的值。为了区别这两类值,需要在 SQL 语句中以不同的方式命名参数,例如:

<asp:SqlDataSource ID="SqlDataSource1" runat="server"

 ConnectionString="<%$ ConnectionStrings:ConnectionString %>"

 SelectCommand="SELECT [productid], [productname], [categoryID], [Price],[InStore],[Description] FROM [products]"

 UpdateCommand="UPDATE [products] SET [productid] = @productid, [productname] = @productname, [categoryid] = @categoryid, [price] = @price,description=@Description WHERE [productid] = @old_productid"

 DeleteCommand="DELETE FROM [products] WHERE [productid] = @ old_productid "/>

 OldValuesParameterFormatString="old_{0}"

在上面例子中,参数名称 @old_productid 用于引用Key字段的原始值24@productid 用于引用新Value字段的新值ISBN001。其中旧值和新值的命名是通过OldValuesParameterFormatString来完成

SqlDataSource OldValuesParameterFormatString 属性也被设置为有效的 .NET Framwork 格式字符串,以指示应该如何重命名 Keys 字典中的参数。当 SqlDataSource ConflictDetection 属性设置为 CompareAllValues 时,此格式字符串还应用于数据绑定控件传递的非键字段的旧值。对于 Delete 操作,SqlDataSource 默认仅应用 Keys 字典(不存在用于删除操作的新值),并使用 OldValuesParameterFormatString 属性的值格式化键参数名称。代码2-14 FormatPara_ObjectDataSource.aspx演示了上面的说明。

        <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" DeleteMethod="DeleteProduct"

            SelectMethod="GetProduct" TypeName="ProductDAL" UpdateMethod="UpdateProduct"

              OldValuesParameterFormatString="old_{0}">

             </asp:ObjectDataSource>

            

        &nbsp;&nbsp;

        <asp:GridView ID="GridView1" DataKeyNames="ProductID" runat="server" AutoGenerateDeleteButton="True"

            AutoGenerateEditButton="True" CellPadding="4" DataSourceID="ObjectDataSource1" AutoGenerateColumns="false"

            Font-Names="Verdana" Font-Size="XX-Small" ForeColor="#333333" GridLines="None"

            >

              <Columns>

                <asp:BoundField DataField="ProductID" HeaderText="ProductID" ReadOnly="True" SortExpression="ProductID"/>

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

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

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

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

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

                </Columns>

          </asp:GridView>

 代码2-14FormatPara_ObjectDataSource.aspx部分源代码

 同时将DeleteUpdate方法改成代码2-15

public   void DeleteProduct(int old_ProductId)

        {

            SqlConnection con = new SqlConnection(_connectionString);

            string deleteString = "DELETE FROM Products WHERE ProductID=@ProductID";

            SqlCommand cmd = new SqlCommand(deleteString, con);

            cmd.Parameters.AddWithValue("@ProductID", old_ProductId);

            con.Open();

            cmd.ExecuteNonQuery();

            con.Close();

        }

        代码2-15 DeleteProduct方法

上面代码的运行结果和图2-33一样。然而在使用上面代码时,可能有些人将2-14的代码写成如下的形式:

   <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" DeleteMethod="DeleteProduct"

            SelectMethod="GetProduct" TypeName="ProductDAL" UpdateMethod="UpdateProduct"

              OldValuesParameterFormatString="old_{0}">

                     <DeleteParameters>

                <asp:Parameter Name="ProductId" Type="Int32" />

            </DeleteParameters>

             </asp:ObjectDataSource>

如果运行此段代码则出现错误如图2-35,这是因为我们需要默认的参数ProductID,如果您显式设置则多此一举,系统认为你需要传递ProductIDold_Product。如果真的要设置应该设置old_Product,也就是

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" DeleteMethod="DeleteProduct"

            SelectMethod="GetProduct" TypeName="ProductDAL" UpdateMethod="UpdateProduct"

              OldValuesParameterFormatString="old_{0}">

                     <DeleteParameters>

                <asp:Parameter Name="old_ProductId" Type="Int32" />

            </DeleteParameters>

             </asp:ObjectDataSource>

这种方法同样使用UpdateMethod

 

2.3.5冲突检测ConflictDetection

正如在前面的主题中所提到的,数据绑定控件在单独的 KeysValues(新值)和 OldValues 字典中将值传递给数据源。默认情况下,SqlDataSource ObjectDataSource 忽略 OldValues 字典,仅应用 Keys Values。这种行为由数据源的 ConflictDetection 属性确定,该属性默认设置为 OverwriteChangesOverwriteChanges 模式本质上意味着仅为了更新或删除记录而匹配主键值。这种行为意味着不管记录的基础值是否已更改,都要更新或删除该记录。通常,仅当行的值准确匹配最初选择的值时才允许 Update Delete 成功是更为适宜的。这样,如果另一个用户在从您选择行到更新该行这段时间内更新了该行,您的更新操作将会失败。

Products表为例,数据库里ProductID24的图书在库数量(InStore)是两本,如下

ProductID     ProductName   CategoryID     Price         InStore     Description

24             生物技术          7             9.0000         2          生物技术丛书

AB两个员工分别负责图书的入库和出库。在某个时间,A员工把新进的5本图书入库,所以它准备更改该记录在库数量为7本,而恰好同时有一读者购买了一本该书,员工B准备更新该记录在库为1本。在A员工正在更新而还没有更新的这段时间里,B也进行了该记录的更新,这样即使A更新了图书为7本由于B接着会将在库图书更新为1本而发生逻辑上错误。为了解决这个问题可以利用“全值匹配”。

数据源通过将 ConflictDetection 属性设置为 CompareAllValues 来支持这种方法。在这种模式下,数据源向命令或方法应用 OldValues 参数,该命令或方法可以在更新或删除记录之前使用这些值确保更新或删除操作匹配该记录的所有这些值。还必须将 OldValuesParameterFormatString 属性设置为有效的 .NET Framework 格式字符串(例如“lodl_{0}”),以指示应该如何重命名 OldValues Keys 字典中的参数以将它们与 NewValues 进行区别。

下面的代码示例演示用于 SqlDataSource 控件的 OverwriteChanges CompareAllValues 模式的典型 SQL 命令。

 update  Products set ProductName=@ProductName,CategoryID=@CategoryID,

 Price=@Price,InStore=@InStore,Description=@Description 

   WHERE (productID = @original_ProductID

 AND  ProductName=@original_ProductName

 and CategoryID=@original_CategoryID

 and price=@original_price

 and InStore=@original_InStore

 and  description=@original_description)

代码2-16Conflict_ObjectDataSource.aspx演示了这种功能的使用(注:为了便于理解,这里使用SqlDataSource进行说明)

<%@ Page Language="C#" %>

<script runat="server">

 protected void SqlDataSource1_Updated(object sender, SqlDataSourceStatusEventArgs e)

 {

    if (e.AffectedRows == 0)

      Response.Write("该行已经变更,您更新失败<br />");

 }

 protected void SqlDataSource1_Deleted(object sender, SqlDataSourceStatusEventArgs e)

 {

    if (e.AffectedRows == 0)

      Response.Write("改行已经变更,您删除失败<br />");

 }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">

<head id="Head1" runat="server">

 <title>Optimistic Concurrency</title>

</head>

<body>

 <form id="form1" runat="server">

    <div>

      <asp:GridView AutoGenerateColumns="False" DataKeyNames="ProductID" DataSourceID="SqlDataSource1"

        ID="GridView1" runat="server" CellPadding="4" Font-Names="Verdana" Font-Size="XX-Small" ForeColor="#333333" GridLines="None">

        <Columns>

          <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />

          <asp:BoundField DataField="ProductID" HeaderText="ProductID" InsertVisible="False"

            ReadOnly="True" SortExpression="ProductID" />

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

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

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

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

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

        </Columns>

         … …

      </asp:GridView>

     

      <asp:SqlDataSource ConnectionString="<%$ ConnectionStrings:ConnectionString %>"

      ID="SqlDataSource1"

        ConflictDetection="CompareAllValues"

         OldValuesParameterFormatString="original_{0}"

         runat="server"

         SelectCommand="GetAllProducts"

        SelectCommandType="StoredProcedure"

        InsertCommand="InsertProduct"

        InsertCommandType="StoredProcedure"

        DeleteCommand="DeleteProduct"

        DeleteCommandType="StoredProcedure"

        UpdateCommand="UpdateProduct"

        UpdateCommandType="StoredProcedure"

        OnUpdated="SqlDataSource1_Updated"

        OnDeleted="SqlDataSource1_Deleted">

      

        <InsertParameters>

          <asp:Parameter Name="ProductName" Type="String" />

          <asp:Parameter Name="CategoryID" Type="Int32" />

          <asp:Parameter Name="Price" Type="decimal" />

          <asp:Parameter Name="InStore" Type="Int16"/>

          <asp:Parameter Name="Description" Type="String" />

          <asp:Parameter Direction="Output" Name="ProductID" Type="Int32" />

        </InsertParameters>

        <DeleteParameters>

         <asp:Parameter Name="original_ProductName" Type="String" />

          <asp:Parameter Name="original_CategoryID" Type="Int32" />

          <asp:Parameter Name="original_InStore" Type="Int16"/>

          <asp:Parameter Name="original_Description" Type="String" />

          <asp:Parameter Name="original_ProductID" Type="Int32" />

        </DeleteParameters>

        <UpdateParameters>

         <asp:Parameter Name="ProductName" Type="String" />

          <asp:Parameter Name="CategoryID" Type="Int32" />

          <asp:Parameter Name="Price" Type="decimal" />

          <asp:Parameter Name="InStore" Type="Int16"/>

          <asp:Parameter Name="Description" Type="String" />

          <asp:Parameter Name="original_ProductName" Type="String" />

          <asp:Parameter Name="original_CategoryID" Type="Int32" />

          <asp:Parameter Name="original_InStore" Type="Int16"/>

          <asp:Parameter Name="original_Description" Type="String" />

          <asp:Parameter Name="original_ProductID" Type="Int32" />

        </UpdateParameters>

      </asp:SqlDataSource>

      <br />

      <asp:DetailsView AutoGenerateRows="False" DataKeyNames="ProductID" DataSourceID="SqlDataSource1"

        DefaultMode="Insert" HeaderText="插入新产品" Height="50px" ID="DetailsView1"

        runat="server" CellPadding="4" Font-Names="Verdana" Font-Size="XX-Small" ForeColor="#333333" GridLines="None" AutoGenerateInsertButton="True" >

        <Fields>

          <asp:BoundField DataField="ProductID" HeaderText="ProductID" InsertVisible="False"

            ReadOnly="True" SortExpression="ProductID" />

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

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

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

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

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

        </Fields>

       … …

      </asp:DetailsView>

    </div>

 </form>

</body>

</html>

   代码2-16 Conflict_ObjectDataSource.aspx的部分代码

在这段代码里,SqlDataSourceUpdateCommand被设置为存储过程,该存储过程的名称为UpdateProduct,具体代码如2-17

ALTER PROCEDURE [UpdateProduct]

 (

 @ProductName varchar(50),

   @CategoryID int,

   @Price decimal,

   @InStore int,

   @Description nvarchar(100),

   @original_ProductID int,

   @original_ProductName varchar(50),

   @original_CategoryID int,

   @original_Price decimal,

   @original_InStore int,

   @original_Description nvarchar(100))

AS

 update  Products set ProductName=@ProductName,CategoryID=@CategoryID,

 Price=@Price,InStore=@InStore,Description=@Description 

   WHERE (productID = @original_ProductID

 AND  ProductName=@original_ProductName

 and CategoryID=@original_CategoryID

 and price=@original_price

 and InStore=@original_InStore

 and  description=@original_description)

           代码2-17 UpdateProduct源代码

同样,DeleteCommand也被设置为存储过程,该存储过程的代码加2-18

ALTER PROCEDURE [DeleteProduct]

 (@original_ProductID int,

   @original_ProductName varchar(50),

   @original_CategoryID int,

   @original_Price decimal,

   @original_InStore int,

   @original_Description nvarchar(100))

AS

 DELETE FROM Products WHERE productID = @original_ProductID

 AND ProductName=@original_ProductName

 and CategoryID=@original_CategoryID

 and price=@original_price

 and InStore=@original_InStore

 and description=@original_description

           代码2-18 DeleteProduct源代码

SelectCommand的存储过程为GetAllProducts,该语句较为简单如2-19

ALTER PROCEDURE GetAllProducts

AS

 select * from Products

              代码2-19 GetAllProducts源代码

为了便于详细列举数据源控件的使用,我们使用了DetailView控件用于插入记录,InsertCommand被设置为存储过程,代码如2-20.

ALTER PROCEDURE [InsertProduct]

 (

   @CategoryID int,

   @Price decimal,

   @InStore int,

   @Description nvarchar(100),

   @ProductID int output

   )

AS

   INSERT INTO products (ProductName,CategoryID,Price,InStore,Description) VALUES

    (@ProductName,@CategoryID,@Price,@InStore,@Description)

 SELECT ProductID = @@IDENTITY

          代码2-20 InsertProduct源代码

若要运行此示例,请在单独的浏览器窗口中打开该示例的两个实例如图2-36


ASP.NET2.0 ObjectDataSource的使用详解(2)
2-36 准备编辑

然后对两个窗口中的相同行单击“Edit”(编辑)按钮,以便将该行置于编辑模式。在第一个窗口中,更改某个行值并单击“Update”(更新),并注意更新是成功的。在第二个窗口中,可以为相同行输入新值并单击 “Update”(更新),但是更新不会成功,这是因为基础行值被第一个更新操作更改了。该示例检测到 Updated Deleted 事件参数的 AffectedRows 属性为 0,从而确认发生了冲突。2-37 一个窗口对数据进行更新

    另外,页面夏布有一个“输入产品”页面,可以利用该页面插入产品数据,如图3-39。在进行插入数据时,由于不会产生冲突,所以不用传递原ProductID等值。


点击此处下载本文源代码:/Files/mqingqing123/ObjectDataSource.rar



上海吃喝玩乐网( http://www.shchwl.net,或者使用  http://www.021happy.com )以及交友网 http://www.021friend.net是有天天设计制作,欢迎访问,也欢迎提出意见和建议。版权所有
posted on 2006-04-20 08:40 天天 阅读(6282) 评论(11)   编辑   收藏 所属分类: ASP.NET V2.0

FeedBack:
#1楼 
2006-04-20 09:59 | 达达
呵呵,我当时在研究怎么用的时候如果有此好文该多好。
谢谢楼主分享!
   回复   引用   查看    
#2楼 218.16.238.*
2006-04-28 21:54 | pcvc.net [未注册用户]
小弟刚学,“为了更好的进行业务处理,我们需要更进一步的封装业务逻辑,以便返回强类型。”不知能否解释一下为何这样做。这么复杂的封装有什么作用,谢谢!
   回复   引用   查看    
#3楼 [TrackBack]
2006-05-01 11:29 | EKing
ASP.NET2.0 ObjectDataSource 非常强大。


[引用提示]EKing引用了该文章, 地址: http://blog.csdn.net/wangyihust/archive/2006/05/01/704225.aspx
   回复   引用   查看    
#4楼 60.22.81.*
2006-05-27 05:21 | 非非 [未注册用户]
楼主您好:

看了楼主的文章,非常受益.只是在实践过程中发现一个问题:

如果在objectdatasource中应用TableAdapter(依照系统的向导生成的),并且TableAdapter中应用的是存贮过程的话,那么在更新过程中出现以下错误:

<b>ObjectDataSource“ObjectDataSource1”未能找到带参数的非泛型方法“Update”: bb, original_aa。
说明: 执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。

异常详细信息: System.InvalidOperationException: ObjectDataSource“ObjectDataSource1”未能找到带参数的非泛型方法“Update”: bb, original_aa。
</b>
而同样的操作,如果TableAdapter中用的是sql语句,则不会出现错误,差不多可以认定是软件的BUG了,不知有无解决办法?


   回复   引用   查看    
#5楼 218.107.203.*
2006-10-25 08:49 | dejack [未注册用户]
未能找到带参数的非泛型方法 不能使用存储过程 BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG BUG
   回复   引用   查看    
#6楼 202.114.154.*
2007-01-07 17:51 | tiger[匿名] [未注册用户]
不是bug,已彻底解决!

主要原因是ObjectDataSource的UPDATE方法对所有字段(包括主健)进行了update操作;而显示控件(如gridview)对主健字段的readonly自动设为true,意味着不会把主健字段回传给ObjectDataSource,造成ObjectDataSource得到的参数个数不一致。
最简单的解决方法是对显示控件(如gridview)的主健字段的readonly改为false,再通过其他方式对主健字段在界面上屏蔽其修改。
[email protected]
   回复   引用   查看    
#7楼 59.35.23.*
2007-03-12 23:36 | stephen [未注册用户]
慢了一年。
》》》》》》》》》》》》》》》》》》》》》》》》》
小弟刚学,“为了更好的进行业务处理,我们需要更进一步的封装业务逻辑,以便返回强类型。”
》》》》》》》》》》》》》》》》》》》》》》》》》

一、 简介

作为开发人员,当我们在学习新技术时,例子可能是我们最大的敌人。而教程往往设计得易于理解,但是同时,它们常常加固了懒惰,低效性,甚至于危险的编码习惯。再也没有比ADO.NET示例更能说明问题的了。在本文中,我们将准备分析一下强类型对象对于你的数据库开发的意义以及为什么在没有例子的情况下你应该在应用程序中尽量使用强类型对象。

具体地说,我们将分析怎样在Visual Studio 2005中创建和使用强类型DataSet。正如本文所探讨的,相对于其它可选的弱类型化的数据存取技术,强类型DataSet提供了很多优点;并且,借助于Visual Studio 2005,创建和使用强类型DataSet比以往更为容易。

二、 强类型对象基础及优点

为了理解强类型化的含义,不妨让我们先考虑一下约会的例子。如果你是一个单身汉,那么,你盼望与什么类型的人约会呢?你可能已经有了自己具体的标准,如富裕且吸引人或可能是居住条件优越而性感。无论你的条件如何,当你决定想与谁在一起呆更长的时间时,你都难免有一套自己的约会标准。如果你很明智,可以列出一个经过事先深思熟虑的列表,这样可以帮助你节约不必要的感情付出。把一项"非酗酒"的条件加入到你的约会标准中将会节约你大量的时间,并且允许你把你的时间和精力用于与更好的候选者约会。

你可能疑惑,这怎么能与编程进行比较呢?请听我的解释。ADO.NET数据存取对象的设计是为了实现最大的灵活性。除非你遇到相当大的麻烦,否则,当你从数据库读取数据时,你都会使用大量普通的未经类型化的对象-当然,.NET框架完全允许这样做。而使用我们的约会类比法,总是把你的关系数据当作泛型对象则有点象承认"我只与满足我条件的人约会"。难道你不能稍微放宽一些条件吗?作为你的朋友,我必须建议你"先确定一些标准,再精简一下你的列表!"也许更好些。

正如忽视筛选你要约会的人能够导致未来的关系问题,与你的对象保持"松耦合"能够给你的代码带来错误。另外,因为如果你让任何旧对象"出入"你的子程序的话,那么,直到你的应用程序在运行时刻执行时,你可能才会知道存在问题。在我们的日期约会类比中,在运行时刻捕获错误很类似于你与你的约会者在一家时髦的意大利餐馆进行一场痛苦而尴尬的讨论。是的,你发现了这个问题;但是,如果你已经事先计划好的话,那么你的结果就不会是"一群用餐者盯着你,而你满身是意大利烤碎肉卷"。如果你简单地把一些更紧密的标准应用于你的代码中,那么在你的应用程序开始运行前(在编译时刻)你就能够捕获错误。例如,请考虑下面的示例代码:

string FirstName = myrow.("FirstName").ToString();

上面的DataRow就是非类型化的;结果,你必须以一个串形式作为你要查询的列名来存取这个值(或者,使用这个列集合中的列的索引值)。很可能,这个列真正存在。一个DataRow列的数据类型是对象;我们假定FirstName列的基本数据类型是字符串,但是,我们必须显式地把它转化成一个字符串以便使用它。如果列名发生变化(例如,你把它改为PersonFirstName),那么编译器是不能通知你的。千万不要这样!因此,如果你的代码看起来更象如下形式,那么你的生活就会更容易些而你的代码将更为可靠:

string FirstName = PersonRow.FirstName;

在第二个例子中,我们拥有一个强类型行,并且我们知道FirstName属性是字符串类型。在此,不会出现杂乱的列名,而且也不存在杂乱的对象转换问题。编译器为我们作类型检查,并且我们可以继续进行其它任务而不必担心是否我们已经正确输入了列名。

对于所有其它列也是如此;总之,当你能够使用一个更为具体的类型时,你永远不应该使用一个泛型对象。但是,请等一下。该强类型对象出自何处?我想我能够告诉你这些对象是为你自动创建的。然而,正如要建立良好的关系需要时间和精力一样,强类型化你的对象也需要付出其它努力。好的方面在于,这里所花费的额外时间是值得的,而且节省了将来更多的花在调试上的时间。

存在若干种可以实现强类型化的方法,在本文余下的部分中,我们将讨论怎样在Visual Studio 2005中创建强类型DataSet;当然,还将分析一下这样做的优点和缺点。

你可能感兴趣的:(dataSource)