gpt4 book ai didi

在ASP.NET 2.0中操作数据之六十九:处理Computed Columns列

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 25 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章在ASP.NET 2.0中操作数据之六十九:处理Computed Columns列由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

导言:

  Microsoft SQL Server里有一种computed columns列.这种列的值是通过一个表达式来计算,而表达式引用的是同一张表的其它列的值.打个比方,有一张ServiceLog表,其包含了ServicePerformed, EmployeeID, Rate, Duration等列. 虽然我们可以在一个web页面或其它什么界面里计算每笔服务的费用(也就是 比率 rate乘以时间段duration),不过我们也可以手动向ServiceLog表添加一个 AmountDue列以反映该信息.我们可以将该列创建为一个普通列,只是任何时候当Rate 或 Duration列的值发生改变时需要更新AmountDue列的值.一个比较好的办法是将AmountDue创建成一个computed column 列,其使用的表达式为 Rate * Duration. 这样,当在一个查询里引用该列时SQL Server就可以自动的计算AmountDue列的值. 。

  由于computed column列的值是由表达式决定的,所以这种列是只读的,并且不能在INSERT 或 UPDATE statements里对其赋值.然而,对使用ad-hoc SQL statements的TableAdapter来说,如果主查询里引用了computed column列,那么自动生成的INSERT 和 UPDATE statements也会自动的引用computed column列.所以,我们必须更新TableAdapter的 INSERT 和 UPDATE 查询,以及InsertCommand 和 UpdateCommand属性,以删除对任何computed column列的引用. 。

  如果在使用 ad-hoc SQL statements的TableAdapter里使用computed columns的话,我们要面临的挑战之一便是,每当完成TableAdapte设置向导时,TableAdapter的 INSERT 和 UPDATE查询都会自动的生成,又再一次的自动引用computed column列.不过如果TableAdapters使用存储过程的话,就不会出现这个问题. 。

  在本文,我们将向Northwind数据库的Suppliers表添加一个computed column列,然后相应地创建一个TableAdapter来处理该表以及该computed column列.我们将在TableAdapter里使用存储过程而不是ad-hoc SQL statements. 。

第一步:向Suppliers表添加一个Computed Column 。

  在本文,我们将向Suppliers表添加一个名为FullContactName的computed column列,它以“ContactName (ContactTitle, CompanyName)”的格式返回contact的name, title,以及所在的公司. 。

  打开服务器资源管理器,在Suppliers表上单击右键,选“Open Table Definition”,这将会显示出表所包含的列以及列的属性,比如数据类型、是否允许为NULL值等等.要添加一个computed column列,只需在表定义里键入表的名称,接下来在Column属性窗口的Computed Column Specification部分的(Formula)文本框里输入表达式(如图1所示)。将该computed column列命名为FullContactName,并使用下面的表达式:

?
1
2
ContactName + ' (' + CASE WHEN ContactTitle IS NOT NULL THEN
  ContactTitle + ', ' ELSE '' END + CompanyName + ')'

  请注意,在SQL里可以用操作符“+” 来连接字符串。而CASE声明类似于传统编程语言里的条件语句。上面代码里的CASE 声明可以这样来理解:如果ContactTitle 不为NULL,那么输出ContactTitle值,再紧接一个逗号;如果为NULL,则无操作。关于CASE 声明的更多信息请参阅文章《The Power of SQL CASE Statements》(http://www.4guysfromrolla.com/webtech/102704-1.shtml) 。

  注意:除了CASE声明外,我们还可以使用ISNULL(ContactTitle, '')。语法ISNULL(checkExpression, replacementValue) returns是这样工作的,如果checkExpression 不为NULL,则对其进行返回;如果为NULL则返回replacementValue.虽然本文这2种语法都可以使用,但是在一些稍微复杂点的情况下,使用ISNULL的情况要多一些.添加完computed column列后,你的屏幕看起来应该和图1差不多:

图1:向Suppliers表添加一个名为FullContactName的Computed Column列 。

  添加完后点工具栏上的Save图标,或按Ctrl+S键,又或者在File菜单里选“保存Suppliers”.“保存”操作会自动地刷新服务器资源管理器,将刚刚添加的的列展现在Suppliers表里.此外,键入到(Formula)文本框的表达式会自动的进行调整,剔除不必要的空白,将列名用[]括起来,并使用圆括号()来显示操作的先后顺序:

?
1
2
(((([ContactName]+ ' (' )+ case when [ContactTitle] IS NOT NULL
  then [ContactTitle]+ ', ' else '' end )+[CompanyName])+ ')' )

  关于Microsoft SQL Server里computed columns列的更多信息请参考文章《technical documentation》(http://msdn2.microsoft.com/en-us/library/ms191250.aspx);同时你也可以参考文章《How to: Specify Computed Columns》(http://msdn2.microsoft.com/en-us/library/ms188300.aspx),看如何一步步地创建computed columns列. 。

  注意:默认情况下,数据库表并没有“实际”(physically)的包含computed columns列,而是每次在一个查询里引用它时重新计算其值.不过,我们可以选择“Is Persisted”选项来让SQL Server实实在在的在数据库表里创建computed columns列.这样的话我们可以为computed column列创建一个索引,当在一个查询的WHERE字句里使用computed column列的值时就可以提高执行效率.更多的信息请参阅文章《Creating Indexes on Computed Columns》(http://msdn2.microsoft.com/en-us/library/ms189292.aspx) 。

第二步:查看Computed Column列的值 。

  在处理数据访问层前,让我们花点时间查看FullContactName列的值.在服务器资源管理器里,在Suppliers表上右键单击,选择“New Query”,这将启动一个查询窗口提示我们在查询里包含哪个表.添加Suppliers表,再点“Close”.接下来从Suppliers表里选择CompanyName, ContactName, ContactTitle,以及FullContactName列.最后,点击工具栏上的红色感叹号图标执行查询,查看结果.如图2所示,结果里包含了FullContactName列,它以ContactName (ContactTitle, CompanyName)”的格式使用了CompanyName, ContactName,ContactTitle这3列. 。

图2:FullContactName列的格式为“ContactName (ContactTitle, CompanyName)” 。

第三步:在数据访问层添加一个SuppliersTableAdapter 。

  为了在我们的应用程序里处理supplier信息,我们首先需要在DAL层创建一个TableAdapter 和 DataTable.我们可以用前面的教程探讨的方法来进行创建,稍微不同的是我们将要与computed columns列打交道. 。

  如果你用ad-hoc SQL statements来构造一个TableAdapter的话,你可以很简单的通过TableAdapter设置向导在TableAdapter的主查询里引用computed column列,这样,在自动生成的INSERT 和 UPDATE statements就会引用computed column列。如果你执行这2个方法的话,将会抛出这样的一个SqlException:“The column ‘ColumnName' cannot be modified because it is either a computed column or is the result of a UNION operator”.虽然我们可以在InsertCommand和UpdateCommand属性里手工改动INSERT 和 UPDATE statement,但是一旦重新运行TableAdapter设置向导后,我们所做的用户定制就会丢失掉. 。

  由于使用ad-hoc SQL statements的TableAdapters的这种不稳定性,我们倾向于使用存储过程来处理computed columns列.如果你使用的是现有的存储过程的话,你可以参阅第66章《在TableAdapters中使用现有的存储过程》那样来配置TableAdapter.如果你使用TableAdapter设置向导来创建存储过程的话,很重要的一点是最开始,你不要在主查询里引用computed columns列,如果你在主查询里引用了computed columns列的话,你刚完成设置,向导就会提示你不能创建相应的存储过程.简而言之,在设置TableAdapter时,最开始不要在主查询里引用computed column列,接下来再对相应的存储过程和TableAdapter的SelectCommand属性进行更改以引用computed column列.这种方法我们在第67章《在TableAdapters中使用JOINs》里探讨过. 。

  本文我们将新添加一个TableAdapter并自动创建存储过程.当然我们要在主查询里忽略这个名为FullContactName的computed column列.打开~/App_Code/DAL文件夹里的NorthwindWithSprocs DataSet数据集,在设计器里右键单击,选“add a new TableAdapter”,这将开启TableAdapter设置向导,指定数据库连接信息(也就Web.config文件里的NORTHWNDConnectionString),点Next。选“Create new stored procedures”项,再点Next. 。

图3:选择“Create new stored procedures”项 。

  接下来我们要指定主查询,键入如下的查询,其返回每个supplier的SupplierID, CompanyName, ContactName, ContactTitle列。注意,我们有意忽略了computed column列(即FullContactName列)。不过我们将在第四步更新该存储过程以引用该列:

?
1
2
SELECT SupplierID, CompanyName, ContactName, ContactTitle
FROM Suppliers

  输入完主查询后点Next,向导要我们为将要创建的4个存储过程命名,分别命名为Suppliers_Select, Suppliers_Insert, Suppliers_Update,以及 Suppliers_Delete。如图4所示:

图4:对自动生成的存储过程命名 。

  接下来要我们为TableAdapter的方法命名并指定用于访问和更新数据的模式.我们全部选中这3项,不过将GetData方法重命名为GetSuppliers.点击Finish完成配置. 。

图5:将GetData方法重命名为GetSuppliers 。

  完成后向导将创建这4个存储过程,并向类型化的DataSet添加ableAdapter以及对应的DataTable. 。

第四步:在TableAdapter的主查询里引用Computed Column列 。

接下来我们将对第三步创建的TableAdapter 和 DataTable进行更新以引用FullContactName列,这要经过2个步骤:

1.更新名为Suppliers_Select的存储过程以返回FullContactName列 。

2.更新DataTable以包含相应的FullContactName列 。

  首先在服务器资源管理器里打开存储过程文件夹,打开Suppliers_Select存储过程,更新其SELECT查询以引用FullContactName列:

?
1
2
SELECT SupplierID, CompanyName, ContactName, ContactTitle, FullContactName
FROM Suppliers

  保存所做的修改.接下来返回到DataSet Designer,在SuppliersTableAdapter上右键单击,选“Configure”.我们可以注意到Suppliers_Select里的Data Columns集里已经包含了FullContactName列. 。

图6:返回到TableAdapter的设置向导更新DataTable的列 。

  点击Finish完成设置,这将自动地为SuppliersDataTable添加相应的列.TableAdapter发觉FullContactName列是一个computed column列,且是只读的.因此将设置该列的ReadOnly属性为true.我们可以进行验证:在SuppliersDataTable里选择该列,打开其属性窗口(如图7),我们注意到FullContactName列的DataType 和 MaxLength属性都作了相应的设置. 。

图7:FullContactName列标记为Read-Only 。

第五步:向TableAdapter添加一个GetSupplierBySupplierID方法 。

  在本文我们将在一个具有更新功能的ASP.NET页面里展示suppliers信息.在前面的文章里,我们从DAL获取指定的记录并将其作为一个强类型的DataTable返回给BLL以做更新,然后将更新后的DataTable再传递给DAL,对数据库做相应的改动.为此,第一步——从DAL返回要更新的记录——我们需要向DAL层添加一个名为GetSupplierBySupplierID(supplierID)的方法. 。

  在DataSet Design设计器里右键单击SuppliersTableAdapter,选“Add Query” ,再选“Create new stored procedure”(可参考3图)。再选“SELECT which returns rows”再点Next. 。

图8:选“SELECT which returns rows”项 。

  接下来为该方法指定查询,键入如下的代码,它将检索某个具体的supplier返回的列与主查询一样. 。

?
1
2
3
SELECT SupplierID, CompanyName, ContactName, ContactTitle, FullContactName
FROM Suppliers
WHERE SupplierID = @SupplierID

  接下来我们将该存储过程命名为Suppliers_SelectBySupplierID,点Next. 。

图9:将存储过程命名为Suppliers_SelectBySupplierID 。

  在接下来的界面,全部选中图里的2项,并将FillBy 和 GetDataBy方法分别命名为FillBySupplierID 和 GetSupplierBySupplierID. 。

图10:将TableAdapter的方法命名为FillBySupplierID 和 GetSupplierBySupplierID 。

  点Finish完成向导 。

第六步:创建业务逻辑层Business Logic Layer 。

  在创建ASP.NET页面前,我们首先要在BLL添加相应的方法.我们将在第7步创建页面,其允许我们查看并编辑suppliers.因此我们在BLL至少要包含2个方法,一个获取所有的suppliers,一个用于更新某个具体的supplier. 。

在~/App_Code/BLL文件夹里创建一个名为SuppliersBLLWithSprocs的新类,添加代码如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindWithSprocsTableAdapters;
 
[System.ComponentModel.DataObject]
public class SuppliersBLLWithSprocs
{
  private SuppliersTableAdapter _suppliersAdapter = null ;
  protected SuppliersTableAdapter Adapter
  {
  get
  {
   if (_suppliersAdapter == null )
   _suppliersAdapter = new SuppliersTableAdapter();
 
   return _suppliersAdapter;
  }
  }
 
  [System.ComponentModel.DataObjectMethodAttribute
  (System.ComponentModel.DataObjectMethodType.Select, true )]
  public NorthwindWithSprocs.SuppliersDataTable GetSuppliers()
  {
  return Adapter.GetSuppliers();
  }
 
  [System.ComponentModel.DataObjectMethodAttribute
  (System.ComponentModel.DataObjectMethodType.Update, true )]
  public bool UpdateSupplier( string companyName, string contactName,
  string contactTitle, int supplierID)
  {
  NorthwindWithSprocs.SuppliersDataTable suppliers =
   Adapter.GetSupplierBySupplierID(supplierID);
  if (suppliers.Count == 0)
   // no matching record found, return false
   return false ;
 
  NorthwindWithSprocs.SuppliersRow supplier = suppliers[0];
 
  supplier.CompanyName = companyName;
  if (contactName == null )
   supplier.SetContactNameNull();
  else
   supplier.ContactName = contactName;
  if (contactTitle == null )
   supplier.SetContactTitleNull();
  else
   supplier.ContactTitle = contactTitle;
 
  // Update the product record
  int rowsAffected = Adapter.Update(supplier);
 
  // Return true if precisely one row was updated, otherwise false
  return rowsAffected == 1;
  }
}

  和其它的BLL class类一样,SuppliersBLLWithSprocs有一个protected Adapter属性,2个public方法:GetSuppliers 和 UpdateSupplier.其中,GetSuppliers方法调用Data Access Layer层对应的GetSupplier方法,该方法将SuppliersDataTable返回给BLL层;而UpdateSupplier方法通过调用DAL层的GetSupplierBySupplierID(supplierID)方法来获取某人具体supplier的信息,然后更新其CategoryName, ContactName,ContactTitle属性,再将修改后的SuppliersRow对象传递给Data Access Layer层的 Update方法,以对数据库做相应的更新. 。

  注意:除了SupplierID 和 CompanyName外,Suppliers表的所有列都允许为NULL值,所以如果传递的contactName 或 contactTitle参数为null的话,我们将分别调用SetContactNameNull 和 SetContactTitleNull方法来将ContactName 和 ContactTitle 属性设置为NULL. 。

第七步: 在表现层处理Computed Column列 。

  做完了所有的必要工作后, 我们将创建一个ASP.NET页面来处理FullContactName列, 打开AdvancedDAL文件夹里的ComputedColumns.aspx 页面,拖一个GridView控件到页面,设其ID为Suppliers,在其智能标签里绑定到一个名为SuppliersDataSource的ObjectDataSource控件,设置其调用SuppliersBLLWithSprocs类,点Next. 。

图11:设置ObjectDataSource调用SuppliersBLLWithSprocs Class类 。

  在SuppliersBLLWithSprocs类里只有2个方法GetSuppliers 和 UpdateSupplier.确保在SELECT 和 UPDATE标签里分别选中这2个方法,点Finish完成设置.完成设置后,Visual Studio将添加相应的BoundField,移除SupplierID列,并将CompanyName, ContactName, ContactTitle,和FullContactName列的HeaderText属性分别设置为“Company”, “Contact Name”, “Title”,“Full Contact Name”,再启用GridView的编辑功能. 。

  Visual Studio将ObjectDataSource控件的OldValuesParameterFormatString属性设置为“original_{0}”. 我们要将其改为默认值“{0}”.如此这般,GridView 和 ObjectDataSource控件的声明代码看起来和下面的差不多:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<asp:GridView ID= "Suppliers" runat= "server" AutoGenerateColumns= "False"
  DataKeyNames= "SupplierID" DataSourceID= "SuppliersDataSource" >
  <Columns>
  <asp:CommandField ShowEditButton= "True" />
  <asp:BoundField DataField= "CompanyName"
   HeaderText= "Company"
   SortExpression= "CompanyName" />
  <asp:BoundField DataField= "ContactName"
   HeaderText= "Contact Name"
   SortExpression= "ContactName" />
  <asp:BoundField DataField= "ContactTitle"
   HeaderText= "Title"
   SortExpression= "ContactTitle" />
  <asp:BoundField DataField= "FullContactName"
   HeaderText= "Full Contact Name"
   SortExpression= "FullContactName"
   ReadOnly= "True" />
  </Columns>
</asp:GridView>
 
<asp:ObjectDataSource ID= "SuppliersDataSource" runat= "server"
  SelectMethod= "GetSuppliers" TypeName= "SuppliersBLLWithSprocs"
  UpdateMethod= "UpdateSupplier" >
  <UpdateParameters>
  <asp:Parameter Name= "companyName" Type= "String" />
  <asp:Parameter Name= "contactName" Type= "String" />
  <asp:Parameter Name= "contactTitle" Type= "String" />
  <asp:Parameter Name= "supplierID" Type= "Int32" />
  </UpdateParameters>
</asp:ObjectDataSource>

  接下来我们在浏览器里登录该页面,如图12所示。每行都有一个FullContactName列,格式为“ContactName (ContactTitle, CompanyName)”. 。

图12:每行展示一个Supplier 。

  点击某行的Edit按钮将导致页面回传,且该行显示为一个编辑界面(如图13),头3行呈现为默认的编辑界面——一个TextBox控件,且其Text属性为该数据域(data field)的值.不过FullContactName列仍然呈现为一个文本框.在Data Source设置向导完成并向GridView控件添加完相应的BoundFields时,FullContactName BoundField的ReadOnly属性为true。我们在第四步注意到,FullContactName列的ReadOnly属性为true,因为TableAdapter意识到该列为一个computed column列. 。

图13:FullContactName列为只读 。

  我们改动这3个列中至少一个列的值,点Update按钮.我们发现FullContactName列的值跟着发生改变. 。

  注意:由于GridView当前用的是BoundFields,导致编辑时用的是默认的界面.又由于CompanyName列是必需的,我们应将其转化成一个TemplateField以包含一个RequiredFieldValidator控件.我将此作为一个练习留给读者,你可以参考第19章《给编辑和新增界面增加验证控件》,看如何一步步的将BoundField转换成 TemplateField,再添加一个确认控件. 。

结语:

  当创建一个表时,Microsoft SQL Server允许我们创建一个computed columns列.这些computed columns列引用该条记录的其它列,再通过一个表达式对其赋值.由于其值来源于一个表达式,因此这种列是只读的,且不能通过INSERT 或 UPDATE statement对其赋值.正是如此,当在一个TableAdapter的主查询里引用computed column的话,要想自动的生成对应的INSERT, UPDATE,和DELETE statements有点麻烦. 。

  在本文,我们探讨了使用computed columns列面临的挑战.具体来说,由于使用ad-hoc SQL statements的TableAdapters自身固有的不稳定性,我们探讨了使用存储过程的情况.当使用TableAdapter向导创建一个新的存储过程的时候,很重要的一点是,最开始不要在主查询里引用任何的computed columns,不然就不能自动的生成对应的存储过程.完成向导后,我们要手动修改SelectCommand属性以引用computed columns列. 。

  祝编程快乐! 。

作者简介 。

  本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的书,是4GuysFromRolla.com的创始人,自1998年以来一直应用 微软Web技术。希望对大家的学习ASP.NET有所帮助.

最后此篇关于在ASP.NET 2.0中操作数据之六十九:处理Computed Columns列的文章就讲到这里了,如果你想了解更多关于在ASP.NET 2.0中操作数据之六十九:处理Computed Columns列的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com