1、第6章 商品属性 第6章商品属性很多在线商店允许购买者自定义他们购买的商品。例如,在销售气球时,通常允许顾客自定义气球的颜色等。本章中,我们将实现产品的属性特征,假定客户端允许顾客下订单时选择气球的颜色。我们先从数据层开始,在数据层先创建一个数据表和存储过程,然后在业务层中调用该存储过程,最后使用这些功能来修改表示层的组件。本章的结尾,将允许顾客选择气球的颜色,如图Figure 6-1所示。Figure 6-1. Products with attributes6.1实现数据层(Implementing the Data Tier)数据层组件支持产品属性特征,包含三个数据表(Attribute
2、, AttributeValue, and ProductAttributeValue) 以及一个存储过程,名称为GetProductAttributeValues。三个数据表如下: Attribute表存储属性的名称,例如Size或者Color。 AttributeValue表包含可能的属性值。在Attribute和AttributeValue数据表之间是一对多的联系。每一个属性例如Color可能有多个和它相联系的值Red, Orange, Yellow等等。我们需要该数据表的目的是帮助我们建立从AttributeID (like 1 for Color) 到其可能的值(Red, Orang
3、e, Yellow, and so on)的AttributeValueIDs的链接 。所以,该数据表需要三个字段: AttributeID对应于Attribute数据表,表示是哪一种属性 (例如color或者size); AttributeValueID字段是integer类型,是唯一的标识列(uniquely identify);Value字段表示属性的描述文本,例如“Orange,” “Red,” “Small,” “Large,”等等。 ProductAttributeValue是一个联系表的实现,对应于Product数据表AttributeValue 数据表的多对多的联系, 该数据表
4、中有字段对 (ProductID, AttributeValueID)。要注意的是,应用不允许产品属性来影响产品的价格。例如,一个白气球和一个黑气球的价格总是相等的。若需要不同的产品价格,你需要创建不同的产品。第5章中通过数据库图表理解了数据表之间的关系。这些数据表之间的联系的可视化表示如图Figure 6-2。Figure 6-2. Diagram describing the relationships among the data tables required to implementproduct attributes下面我们修改BalloonShop数据库,然后通过练习实现一个新的
5、存储过程CatalogGetProductAttributeValues。练一练: 为产品属性创建数据层的功能1. 打开SQL Server Management Studio并执行如下代码,该代码将创建并构造Attribute数据表 (你也可以通过Visual Web Developer来创建存储过程)。- Connect to the BalloonShop databaseUSE BalloonShop- Create attribute table (stores attributes such as Size and Color)CREATE TABLE Attribute (Att
6、ributeID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,Name NVARCHAR(100) NOT NULL - e.g. Color, Size)- Populate Attribute tableSET IDENTITY_INSERT Attribute ONINSERT INTO Attribute (AttributeID, Name)VALUES (1, Color);SET IDENTITY_INSERT Attribute OFF注意:我们使用SET IDENTITY_INSERT ON(注意其中的下划线,原教材上没有),目的是在为数据表
7、添加数据时,可以让我们自己来指定标识列的值。除了此种情形之外,当一个字段是标识列(IDENTITY)字段时, 数据库会为我们自动产生该标识列的ID值。2. 使用如下代码来创建并填充AttributeValue table数据表:- Create AttributeValue table (stores values such as Yellow or XXL)CREATE TABLE AttributeValue (AttributeValueID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,AttributeID INT NOT NULL, - The ID
8、 of the attributeValue NVARCHAR(100) NOT NULL - E.g. Yellow)- Set the IDENTITY INSERT option for AttributeValueSET IDENTITY_INSERT AttributeValue ON;- Populate AttributeValue tableINSERT INTO AttributeValue (AttributeValueID, AttributeID, Value)SELECT 1, 1, White UNION ALLSELECT 2, 1, Black UNION AL
9、LSELECT 3, 1, Red UNION ALLSELECT 4, 1, Orange UNION ALLSELECT 5, 1, Yellow UNION ALLSELECT 6, 1, Green UNION ALLSELECT 7, 1, Blue UNION ALLSELECT 8, 1, Indigo UNION ALLSELECT 9, 1, Purple;- Set the IDENTITY INSERT option for AttributeValueSET IDENTITY_INSERT AttributeValue OFF;3. 使用如下的代码创建并填充Produc
10、tAttributeValue数据表:- Create ProductAttributeValue table (associates attribute values toproducts)CREATE TABLE ProductAttributeValue (ProductID INT NOT NULL,AttributeValueID INT NOT NULL,PRIMARY KEY (ProductID, AttributeValueID)- Populate ProductAttributeValue tableINSERT INTO ProductAttributeValue (P
11、roductID, AttributeValueID)SELECT p.ProductID, av.AttributeValueIDFROM product p, AttributeValue av;4. 执行如下代码,该代码将创建外键并建立Figure 6-2所示的数据表之间的联系。- Create the foreign keysALTER TABLE AttributeValueADD CONSTRAINT FK_AttributeValue_AttributeFOREIGN KEY(AttributeID)REFERENCES Attribute (AttributeID)GOALTE
12、R TABLE ProductAttributeValueADD CONSTRAINT FK_ProductAttributeValue_AttributeValueFOREIGN KEY(AttributeValueID)REFERENCES AttributeValue (AttributeValueID)GOALTER TABLE ProductAttributeValue WITH CHECKADD CONSTRAINT FK_ProductAttributeValue_ProductFOREIGN KEY(ProductID)REFERENCES Product (ProductID
13、)GO注意上面代码中的下划线,原教材中没有。5. 执行如下的SQL代码,将创建CatalogGetProductAttributeValues存储过程。该存储过程将属性和一个产品相关联。- Create CatalogGetProductAttributeValues stored procedureCREATE PROCEDURE CatalogGetProductAttributeValues(ProductId INT)ASSELECT a.Name AS AttributeName,av.AttributeValueID,av.Value AS AttributeValueFROM A
14、ttributeValue avINNER JOIN attribute a ON av.AttributeID = a.AttributeIDWHERE av.AttributeValueID IN(SELECT AttributeValueIDFROM ProductAttributeValueWHERE ProductID = ProductID)ORDER BY a.Name;解释该例是如何工作的: 产品属性的数据逻辑我们将讨论创建并填充AttributeValue、ProductAttributeValue 数据表以及CatalogGetProductAttributeValues存
15、储过程的代码。在讨论之前,确保你理解了Attribute, AttributeValue, 和 ProductAttributeValue 数据表的每一个字段的含义以及填充这些数据表的代码。我们先看看AttributeValue数据表。为了填充该数据表, 我们不是使用多个INSERT 语句, 而是使用INSERT.SELECT语句, 该语句将允许我们插入由SELECT语句返回的结果。然而,我们不是使用简单的SELECT语句来获取数据,我们使用了多个SELECT语句使用了UNION ALL。总之,我们不是使用如下代码:- Populate AttributeValue tableINSERT I
16、NTO AttributeValue (AttributeValueID, AttributeID, Value)VALUES (1, 1, White);INSERT INTO AttributeValue (AttributeValueID, AttributeID, Value)VALUES (2, 1, Black);INSERT INTO AttributeValue (AttributeValueID, AttributeID, Value)VALUES (3, 1, Red); .而是使用如下代码:- Populate AttributeValue tableINSERT INT
17、O AttributeValue (AttributeValueID, AttributeID, Value)SELECT 1, 1, White UNION ALLSELECT 2, 1, Black UNION ALLSELECT 3, 1, Red UNION ALL .如果你运行SQL Server 2008, 你可以使用更酷的方式来向数据表中插入多个值。例如可以使用如下方式:- Populate AttributeValue table - SQL SERVER 2008 syntaxINSERT INTO AttributeValue (AttributeValueID, Attr
18、ibuteID, value)VALUES (1, 1, White), (2, 1, Black), (3, 1, Red), .当填充ProductAttributeValue数据表时,目的是想关联所有已存在属性值 (通过AttributeValueID字段) 到每一个产品(通过ProductID字段)。在应用中,我们的产品是气球,我们想销售每一种可能颜色的产品。填充ProductAttributeValue数据表的代码是使用INSERT INTO命令,该命令插入由SELECT查询得到的很多记录:INSERT INTO ProductAttributeValue (ProductID, A
19、ttributeValueID)在我们的例子中,插入到ProductAttributeValue数据表的SELECT查询是一个交叉连接。这种连接将产生两个集合的笛卡尔积(Cartesian product)。结果是产生两个数据集的所有可能的组合。例如,集合1, 2, 3和a, b, c的笛卡尔积, 在数学上写为1, 2, 3 a, b, c, 将会产生如下的数据集: 1, a, 1, b, 1, c, 2, a, 2, b, 2, c, 3, a, 3, b, 3, c 。在我们的例子中,如果创建已存在的产品IDs值和已存在的属性值的IDs值的笛卡尔积,我们将获得形式为(ProductID,
20、AttributeValueID)元素的列表, 这就是我们想增加到ProductAttributeValue数据表中的记录。实现该交叉连接的SQL语法是:INSERT INTO ProductAttributeValue (ProductID, AttributeValueID)SELECT p.ProductID, av.AttributeValueIDFROM product p, AttributeValue av;例子数据中包含9个属性值和62个产品。交叉连接操作将产生558 (即9乘以62)条记录到ProductAttributeValue数据表中。注意使用SELECT语句来创建交叉
21、连接不是标准SQL, 虽然这种形式为大多数数据库服务器认可。正规的实现交叉查询的语法是使用CROSS JOIN语法:INSERT INTO ProductAttributeValue (ProductID, AttributeValueID)SELECT p.ProductID, av.AttributeValueIDFROM product p CROSS JOIN AttributeValue av;如果不使用交叉连接(cross join), 我们也可以使用前面介绍的UNION方法。UNION将多个SELECT语句的结果累加成一个单独的结果集。例如,如果将两个各有5条记录的查询进行联合(
22、UNION),结果集将有10条记录。当然,对于UNION来说, 所有的查询必须返回类型相容、数量相同的字段,我们在这里不将详细讨论UNION。例如,使用UNION,你可以仅仅增加特定的属性值到产品中,如下面的代码所示:- Populate ProductAttributeValue tableINSERT INTO ProductAttributeValue (ProductID, AttributeValueID)SELECT ProductID, 1 AS AttributeValueID FROM productUNION ALL SELECT ProductID, 2 AS Attri
23、buteValueID FROM productUNION ALL SELECT ProductID, 3 AS AttributeValueID FROM productUNION ALL SELECT ProductID, 4 AS AttributeValueID FROM productUNION ALL SELECT ProductID, 5 AS AttributeValueID FROM productUNION ALL SELECT ProductID, 6 AS AttributeValueID FROM productUNION ALL SELECT ProductID,
24、7 AS AttributeValueID FROM productUNION ALL SELECT ProductID, 8 AS AttributeValueID FROM productUNION ALL SELECT ProductID, 9 AS AttributeValueID FROM product;最后,我们看看存储过程CatalogGetProductAttributeValues。该存储过程接受一个参数ProductID,并返回产品属性的列表。- Create CatalogGetProductAttributeValues stored procedureCREATE
25、PROCEDURE CatalogGetProductAttributeValues(ProductId INT)AS存储过程中的SQL代码返回带有AttributeName, AttributeValueID, and AttributeValue的产品列表:SELECT a.Name AS AttributeName,av.AttributeValueID,av.Value AS AttributeValueFROM AttributeValue avINNER JOIN attribute a ON av.AttributeID = a.AttributeIDWHERE av.Attri
26、buteValueID IN(SELECT AttributeValueIDFROM ProductAttributeValueWHERE ProductID = ProductID)ORDER BY a.Name;你可以使用SQL Server Management Studio来测试该存储过程, 用值来替换参数的名字。例如,你可以用一个产品的ID值来替换参数ProductID,并执行该存储过程。相应地,你也可以通过EXEC命令在SQL Server Management Studio中执行存储过程(如下面的代码片段所示), 或者右键点击存储过程在Visual Web Developer或者
27、SQL Server Management Studio中, 并选择执行存储过程。EXEC CatalogGetProductAttributeValues 1该命令将返回具有产品ID值为1的结果。如下图所示。6.2实现业务层(Implementing the Business Tier)P207实现业务层只需要编写调用CatalogGetProductAttributeValues存储过程的代码即可。 增加如下的代码到CatalogAccess类中, 在App_Code/CatalogAccess.cs文件中。/ Retrieve the list of product attributes
28、public static DataTable GetProductAttributes(string productId)/ get a configured DbCommand objectDbCommand comm = GenericDataAccess.CreateCommand();/ set the stored procedure namecomm.CommandText = CatalogGetProductAttributeValues;/ create a new parameterDbParameter param = comm.CreateParameter();pa
29、ram.ParameterName = ProductID;param.Value = productId;param.DbType = DbType.Int32;comm.Parameters.Add(param);/ execute the stored procedure and return the resultsreturn GenericDataAccess.ExecuteSelectCommand(comm);6.3实现表示层(Implementing the Presentation Tier)创建表示层的含义是增加相应的控件以允许用户从产品属性列表中选择相应的值。属性值不是通
30、过硬编码得到的,而是从数据库中读取的,这样在数据库中的属性值修改就可以直接反映到表示层中。练一练: 实现产品属性表示层1. 将ProductsList.ascx中Price处的代码修改为如下:Price:2. 增加如下的样式到BalloonShop.css文件中。.DetailSection margin-top: 10px;margin-bottom: 10px;3. 将ProductsList.ascx转换到Design View, 选择DataList控件, 打开其属性窗口(使用 F4快捷键)。在属性窗口中选择事件图标,双击ItemDataBound事件实体。4. 增加如下的事件代码。(
31、注意需要导入System.Data名称空间,方法是在文件的开始位置使用using System.Data;语句)。using System.Data;/ Executed when each item of the list is bound to the data sourceprotected void list ItemDataBound(object sender, DataListItemEventArgs e)/ obtain the attributes of the productDataRowView dataRow = (DataRowView) e.Item.DataItem;string productId = d
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1