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>
<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
<script type=text/javascript> // </script>
FeedBack:
#2楼 [未注册]
#3楼 [未注册][TrackBack]
2006-05-01 11:29 |
EKing
ASP.NET2.0 ObjectDataSource 非常强大。
[引用提示]EKing引用了该文章, 地址: http://blog.csdn.net/wangyihust/archive/2006/05/01/704225.aspx
回复 引用 查看
[引用提示]EKing引用了该文章, 地址: http://blog.csdn.net/wangyihust/archive/2006/05/01/704225.aspx
回复 引用 查看
#4楼 [未注册]
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了,不知有无解决办法?
回复 引用 查看
看了楼主的文章,非常受益.只是在实践过程中发现一个问题:
如果在objectdatasource中应用TableAdapter(依照系统的向导生成的),并且TableAdapter中应用的是存贮过程的话,那么在更新过程中出现以下错误:
<b>ObjectDataSource“ObjectDataSource1”未能找到带参数的非泛型方法“Update”: bb, original_aa。
说明: 执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。
异常详细信息: System.InvalidOperationException: ObjectDataSource“ObjectDataSource1”未能找到带参数的非泛型方法“Update”: bb, original_aa。
</b>
而同样的操作,如果TableAdapter中用的是sql语句,则不会出现错误,差不多可以认定是软件的BUG了,不知有无解决办法?
回复 引用 查看
#5楼 [未注册]
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楼 [未注册]
#7楼 [未注册]
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;当然,还将分析一下这样做的优点和缺点。
http://www.openitpower.com/wenzhang/109/14858_1.html
回复 引用 查看
》》》》》》》》》》》》》》》》》》》》》》》》》
小弟刚学,“为了更好的进行业务处理,我们需要更进一步的封装业务逻辑,以便返回强类型。”
》》》》》》》》》》》》》》》》》》》》》》》》》
一、 简介
作为开发人员,当我们在学习新技术时,例子可能是我们最大的敌人。而教程往往设计得易于理解,但是同时,它们常常加固了懒惰,低效性,甚至于危险的编码习惯。再也没有比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;当然,还将分析一下这样做的优点和缺点。
http://www.openitpower.com/wenzhang/109/14858_1.html
回复 引用 查看
#8楼 [未注册]
#9楼 [未注册][TrackBack]
2007-08-25 00:03 |
Jackie
ASP.NET2.0ObjectDataSource的使用详解(1)本系列文章将介绍ObjectDataSource的使用,为了内容的完成性,所以虽然简单,但是还是发到首页,不知道行不行本...
[引用提示] Jackie引用了该文章, 地址: http://www.cnblogs.com/lly072041/archive/2007/08/25/868986.html
[引用提示] Jackie引用了该文章, 地址: http://www.cnblogs.com/lly072041/archive/2007/08/25/868986.html