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

4 绑定到业务逻辑
在上面 GetProduct 的定义时,可以看到该方法返回的类型是 SqlDataReader ,由于 ObjectDataSource 将来需要作为绑定控件的数据来源,所以它的返回类型必须如下的返回类型之一:
Ienumerable DataTable DataView DataSet 或者 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 DataKeyNames OldValuesParameterFormatString
1 DataKeyNames
如果读者使用 Simple_ObjectDataSource.aspx ,可以发现如果没有设置 GridView DataKeynames 属性,则无法更新或者删除操作。
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 设置为 ReadOnly true ,自然的传递到 UpdateProduct Key/Value 的值为:
{"@ProductName",          " 生物技术 "}
{"@CategoryID",            "7"      }
{"@price",                   "9.0000"}
{"@InStore",                "2"      }
{"@Description",             " 生物技术丛书 "}
这里可以看到没有了 @ProductID 列,那么系统如何知道你当前编辑的 ProductID 呢?这个功能就是由 DataKeyNames 来完成。原来当您将 GridView DataKeyNames 设置为 ProductID 时,该列在更新时会自动调用 ProductID 的数值。
 
在删除方法 DeleteProduct 里,如果您设置断点查看 pro 的值,您会发现在删除产品里仅仅传递 DataKeyname 的值(也就是仅仅传递 ProductID ),而并不传递 ProductName CategoryID 等的值,所以您会发现,对于编辑我定义的方式是:
public Product(int productID, string productName, int categoryID, decimal price, int instore, string description)
对于删除,我定义的方式是
public   void DeleteProduct(int ProductId)
就是这个原因。
 
2 OldValuesParameterFormatString
在使用前面的例子里,请注意分配给 UpdateCommand Update 语句中的参数的命名约定。 Update Delete 的参数都采用默认的列命名方式,例如 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



                
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 为例,当将 ProductID ReadOnly 设置为 ”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 更新为什么值。因为这样我们就需要区分 Key value 的值。为了区别这两类值,需要在 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 ,如果您显式设置则多此一举,系统认为你需要传递 ProductID old_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
正如在前面的主题中所提到的,数据绑定控件在单独的 Keys Values (新值)和 OldValues 字典中将值传递给数据源。默认情况下, SqlDataSource ObjectDataSource 忽略 OldValues 字典,仅应用 Keys Values 。这种行为由数据源的 ConflictDetection 属性确定,该属性默认设置为 OverwriteChanges OverwriteChanges 模式本质上意味着 仅为了更新或删除记录而匹配主键值 。这种行为意味着不管记录的基础值是否已更改,都要更新或删除该记录。通常,仅当行的值准确匹配最初选择的值时才允许 Update Delete 成功是更为适宜的。这样,如果另一个用户在从您选择行到更新该行这段时间内更新了该行,您的更新操作将会失败。
Products 表为例,数据库里 ProductID 24 的图书在库数量( InStore )是两本,如下
ProductID     ProductName   CategoryID     Price         InStore     Description
24             生物技术           7             9.0000         2           生物技术丛书
A B 两个员工分别负责图书的入库和出库。在某个时间, 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-16 Conflict_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 的部分代码
在这段代码里, SqlDataSource UpdateCommand 被设置为存储过程,该存储过程的名称为 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


2-36 准备编辑
 
然后对两个窗口中的相同行单击 “Edit” (编辑)按钮,以便将该行置于编辑模式。在第一个窗口中,更改某个行值并单击 “Update” (更新),并注意更新是成功的。在第二个窗口中,可以为相同行输入新值并单击 “Update” (更新),但是更新不会成功,这是因为基础行值被第一个更新操作更改了。该示例检测到 Updated Deleted 事件参数的 AffectedRows 属性为 0 ,从而确认发生了冲突。 2-37 一个窗口对数据进行更新
 
    另外,页面夏布有一个“输入产品”页面,可以利用该页面插入产品数据,如图 3-39 。在进行插入数据时,由于不会产生冲突,所以不用传递原 ProductID 等值。
 
 

点击此处下载本文源代码:/Files/mqingqing123/ObjectDataSource.rar
 
posted on 2006-04-20 08:40 天天 阅读(4084) 评论(9)   编辑   收藏 所属分类: ASP.NET V2.0
<script type=text/javascript> // </script>

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


[引用提示]EKing引用了该文章, 地址: http://blog.csdn.net/wangyihust/archive/2006/05/01/704225.aspx
   回复   引用   查看    
# re: ASP.NET2.0 ObjectDataSource的使用详解(2)
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了,不知有无解决办法?


   回复   引用   查看    
# re: ASP.NET2.0 ObjectDataSource的使用详解(2)
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
   回复   引用   查看    
# re: ASP.NET2.0 ObjectDataSource的使用详解(2)
2007-01-07 17:51 | tiger[匿名]
不是bug,已彻底解决!

主要原因是ObjectDataSource的UPDATE方法对所有字段(包括主健)进行了update操作;而显示控件(如gridview)对主健字段的readonly自动设为true,意味着不会把主健字段回传给ObjectDataSource,造成ObjectDataSource得到的参数个数不一致。
最简单的解决方法是对显示控件(如gridview)的主健字段的readonly改为false,再通过其他方式对主健字段在界面上屏蔽其修改。
wsh@hubu.net
   回复   引用   查看    
# re: ASP.NET2.0 ObjectDataSource的使用详解(2)[未登录]
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;当然,还将分析一下这样做的优点和缺点。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值