ASP.NET2.0 ObjectDataSource的使用详解

本系列文章将介绍ObjectDataSource的使用,为了内容的完成性,所以虽然简单,但是还是发到首页,不知道行不行
本系列文章主要参考MSDN,ASP.NET快速入门和ASP.NET的文章整理而成,将由浅入深说明ObjectDataSource的使用,仅供参考,不保证内容100%的争取


1
SqlDataSource ObjectDataSource 控件的比较
ASP.NET2.0提供了 SqlDataSource 数据源控件,后者支持用于指定连接字符串、 SQL 语句或存储过程的属性,用以查询或修改数据库。但是, SqlDataSource 控件存在一个问题:该控件的缺点在于它迫使您将用户界面层与业务逻辑层混合在一起。然而随着应用程序规模的扩大,您会越来越感觉到混合多个层的做法是不可取的。 生成严格意义上的多层 Web 应用程序时,您应该具有清晰的用户界面层、业务逻辑层和数据访问层。仅仅由于 SqlDataSource 控件的强制而在用户界面层引用 SQL 语句或存储过程是不可取的。
SqlDataSource ObjectDataSource 的选择,从这某中意义上说,前者适合大多数小规模的个人或业余站点,而对于较大规模的企业级应用程序,在应用程序的呈现页中直接存储 SQL 语句可能很快就会变得无法维护。这些应用程序通常需要用中间层数据访问层或业务组件构成的封装性更好的数据模型。所以使用 ObjectDataSource 控件是一种较为明智和通用的做法。
 
2 ObjectDataSource 的概述
ObjectDataSource 控件对象模型类似于 SqlDataSource 控件。 ObjectDataSource 公开一个 TypeName 属性(而不是 ConnectionString 属性),该属性指定要实例化来执行数据操作的对象类型(类名)。类似于 SqlDataSource 的命令属性, ObjectDataSource 控件支持诸如 SelectMethod UpdateMethod InsertMethod DeleteMethod 的属性,用于指定要调用来执行这些数据操作的关联类型的方法。本节介绍一些方法,用于构建数据访问层和业务逻辑层组件并通过 ObjectDataSource 控件公开这些组件。 下面是该控件的声明方式:
<asp:ObjectDataSource
    CacheDuration="string|Infinite"    CacheExpirationPolicy="Absolute|Sliding"
    CacheKeyDependency="string"
    ConflictDetection="OverwriteChanges|CompareAllValues"
    ConvertNullToDBNull="True|False"    DataObjectTypeName="string"
    DeleteMethod="string"    EnableCaching="True|False"
    EnablePaging="True|False"    EnableTheming="True|False"
    EnableViewState="True|False"    FilterExpression="string"
    ID="string"    InsertMethod="string"
    MaximumRowsParameterName="string"
    OldValuesParameterFormatString="string"
    OnDataBinding="DataBinding event handler"
    OnDeleted="Deleted event handler"    OnDeleting="Deleting event handler"
    OnDisposed="Disposed event handler"    OnFiltering="Filtering event handler"
    OnInit="Init event handler"    OnInserted="Inserted event handler"
    OnInserting="Inserting event handler"    OnLoad="Load event handler"
    OnObjectCreated="ObjectCreated event handler"
    OnObjectCreating="ObjectCreating event handler"
    OnObjectDisposing="ObjectDisposing event handler"
    OnPreRender="PreRender event handler"    OnSelected="Selected event handler"
    OnSelecting="Selecting event handler"    OnUnload="Unload event handler"
    OnUpdated="Updated event handler"    OnUpdating="Updating event handler"
    runat="server"    SelectCountMethod="string"
    SelectMethod="string"    SortParameterName="string"
    SqlCacheDependency="string"    StartRowIndexParameterName="string"
    TypeName="string"    UpdateMethod="string"
 
>
    <DeleteParameters>
                <asp:ControlParameter        ControlID="string"
                   ConvertEmptyStringToNull="True|False"
                    DefaultValue="string"
                   Direction="Input|Output|InputOutput|ReturnValue"
                    Name="string"
                    PropertyName="string"
                    Size="integer"
                    Type="Empty|Object|DBNull|Boolean|Char|SByte|
                        Byte|Int16|UInt16|Int32|UInt32|Int64|UInt64|
                        Single|Double|Decimal|DateTime|String"
                />
                <asp:CookieParameter                 CookieName="string"          />
                <asp:FormParameter                   FormField="string"    />
                <asp:Parameter                          Name="string" />
                <asp:ProfileParameter               PropertyName="string" />
                <asp:QueryStringParameter      QueryStringField="string" />
                <asp:SessionParameter          SessionField="string" />
        </DeleteParameters>
        <FilterParameters>... ...</FilterParameters>
        <InsertParameters>... ...</InsertParameters>
        <SelectParameters>... ...</SelectParameters>
        <UpdateParameters>... ...</UpdateParameters>
</asp:ObjectDataSource>
 
3 绑定到数据访问层
数据访问层组件封装 ADO.NET 代码以通过 SQL 命令查询和修改数据库。它通常提炼创建 ADO.NET 连接和命令的详细信息,并通过可使用适当的参数调用的方法公开这些详细信息。典型的数据访问层组件可按如下方式公开:
public class MyDataBllLayer { 
public DataView GetRecords(); 
public int UpdateRecord(int recordID, String recordData);
public int DeleteRecord(int recordID);
public int InsertRecord(int recordID, String recordData);
}
也就是 , 通常是在业务逻辑访问层定义对数据库里记录的操作,上面就定义了 GetRecords UpdateRecord DeleteRecord InsertRecord 四个方法来读取、更新、删除和插入数据库里的数据,这些方法基本上是根据 SQL 里的 Select Update Delete Insert 语句而定义。
和上面方法相对应, ObjectDataSource 提供了四个属性来设置该控件引用的数据处理,可以按照如下方式关联到该类型,代码如下
 <asp:ObjectDataSource TypeName="MyDataLayer"   runat="server"
     SelectMethod="GetRecords"
UpdateMethod="UpdateRecord" 
    DeleteMethod="DeleteRecord"
InsertMethod="InsertRecord"
/>
    这里的 SelectMethon 设置为 MyDataBllLayer 里的 GetRecords() 方法,在使用时需要注意 ObjectDataSource 旨在以声明的方式简化数据的开发,所以这里设置 SelectMethod 的值为 GetRecords 而不是 GetRecords()
同样依次类推, UpdateMethod DeleteMethod InsertMethod 分别对应的是 UpdateRecord
DeleteRecord InsertRecord 方法。
    在上面 GetRecords ()的定义时,读者可以看到该方法返回的类型是 DataView ,由于 ObjectDataSource 将来需要作为绑定控件的数据来源,所以它的返回类型必须如下的返回类型之一:
Ienumerable DataTable DataView DataSet 或者 Object
     除此以外, ObjectDataSource 还有一个重要的属性 TypeName ObjectDataSource 控件使用反射技术来从来从业务逻辑程序层的类对象调用相应的方法,所以 TypeName 的属性值就是用来标识该控件工作时使用的类名称,下面通过 Simple_ObjectDataSource.aspx 来说明 ObjectDataSource 的基本使用。
1 )建立数据业务逻辑层
   为了方装业务逻辑我建立了 ProductDAL.cs 文件。在该文件里定义了 GetProduct 方法获取产品列表, UpdateProduct 方法更新产品记录, DeleteProduct 删除产品记录,为了便于共享,将该文件放置在 App_Code 目录下,完整代码如1 -1
 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 ProductBLL
/// </summary>
public class ProductDAL
{
    protected int _count = -1;
    public ProductDAL()
    {   }
 
     string   _connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
 
    public SqlDataReader GetProduct()
    {
        SqlConnection con = new SqlConnection(_connectionString);
        string selectString = "SELECT * FROM Products";
        SqlCommand cmd = new SqlCommand(selectString, con);
        con.Open();
        SqlDataReader dtr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
        return dtr;
         }
 
 
    public void UpdateProduct(int productID, string productName, int categoryID, decimal price, Int16 inStore,string description)
    {
        SqlConnection con = new SqlConnection(_connectionString);
        string updateString = "UPDATE Products set ProductName=@ProductName,CategoryID=@CategoryID,Price=@Price,InStore=@InStore,Description=@Description where ProductID=@ProductID";
        SqlCommand cmd = new SqlCommand(updateString, con);
        cmd.Parameters.AddWithValue("@ProductID",productID);
        cmd.Parameters.AddWithValue("@ProductName",productName);
        cmd.Parameters.AddWithValue("@CategoryID",categoryID);
        cmd.Parameters.AddWithValue("@Price",price);
        cmd.Parameters.AddWithValue("@InStore",inStore);
        cmd.Parameters.AddWithValue("@Description",description);
        con.Open();
        cmd.ExecuteNonQuery();
        con.Close();
    }
 
 
    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();
        }
}
          代码1 -1 ProductDAL.cs 源文件
  
 
2 )建立表示层
  建立一个页面 Simple_ObjectDataSource.aspx 然后将 ObjectDataSource 控件托方到 Web 窗体创,使用默认的 ID Visual Stduio.NET2005 为我们建立业务逻辑提供了强大的支持。选中 ObjectDataSource1 ,在其智能配置里选择配置数据源,弹出配置向导如图 2-1

                                     图
2-1ObjectDataSource 配置向导
 
  此时系统会枚举已经存在的类,选择 ProductDAL ,单击“ Next ”,进入“ Define Data Methods ”页面如图 2-2 。在此页面需要单独设置 Select Update 、和 Delete 分别如下图


2-30
设置 Select 属性对应的方法 GetProduct


2-31
设置 Update 属性对应的方法 UpdateProduct


2-32 设置 Delete 属性对应的方法 DeleteProduct
 
通过上面的设置系统自动生成如下代码如下
  <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" DeleteMethod="DeleteProduct"
            SelectMethod="GetProduct" TypeName="ProductDAL" UpdateMethod="UpdateProduct">
            <DeleteParameters>
                <asp:Parameter Name="ProductId" Type="Int32" />
            </DeleteParameters>
            <UpdateParameters>
                <asp:Parameter Name="productID" Type="Int32" />
                <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" />
            </UpdateParameters>
        </asp:ObjectDataSource>
         代码 2-11 Simple_ObjectDataSource.aspx 部分源代码
2-33 显示了运行结果,此时我们可以编辑或者删除现有的产品记录。


       
2-33 Simple_ObjectDataSource.aspx 运行结果
 
注意:如果能够进行编辑、删除,你需要将GridViewDataKeyNames设置为数据库里的主键名。具体后面会说明。
 
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 等值。
 
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值