|
系列文章导航:
WCF版的PetShop之三:实现分布式的Membership和上下文传递
上一篇文章主要讨论的是PetShop的模块划分,在这一篇文章中我们来讨论在一个模块中如何进行层次划分。模块划分应该是基于功能的,一个模块可以看成是服务于某项功能的所有资源的集合;层次划分侧重于关注点分离(SoC:Separation of Concern ),让某一层专注于某项单一的操作,以实现重用性、可维护性、可测试性等相应的目的。Source Code从这里下载。
一、基本的层次结构
我们接下来将目光聚焦到模块内部,看看每一个模块具体又有怎样的层次划分。我们将Infrastructures、Products和Orders目标展开,将会呈现出如图1所示的层次结构。
图1 从解决方案的结构看模块的层次结构
以Products模块为例,它由如下的项目组成:
- Products:对于整个应用来说,Products是最终基于该模块功能的提供者;
- Products.Interface: 模块提供给其他模块的服务接口,本项目被Products项目引用;
- Products.Service.Interface:模块客户端和服务端进行服务调用的WCF服务契约,Products项目最为WCF服务的客户端通过该接口进行服务调用;
- Products.Service:实现了上述服务契约的WCF服务,引用了Products.Service.Interface项目;
- Products.BusinessComponent:也可以称为业务逻辑层,实现了真正的业务逻辑;
- Products.DataAccess:数据访问层,在这里主要提供对数据库的访问;
- Products.BusinessEntity:提供的业务实体(BusinessEntity)类型的定义。一般来讲,业务实体和数据契约(DataContract)是不同的,前者主要对本模块,后者则对外,在这里为了简单起见,将两者合二为一。
从部署的角度讲,Products和Products.Interface部署与于Web服务器;Products.Service、Products.BusinessComponent和Products.DataAccess则部署于应用服务器;Products.Service.Interface和Products.BusinessEntity则同时被部署于Web服务器和应用服务器。整个层次结构大体上如图2所示。
图2 逻辑层次和物理部署
二、数据库设计
整个应用主要涉及4个表,其中3个用于存储业务数据(产品表、订单表和订单明细表),另一个用于存储简单的审核信息(审核表)。4个表的结构可以分别参考相应的SQL脚本。
产品表(T_PRODUCT)
1: CREATE TABLE [T_PRODUCT] (
2: [PRODUCT_ID] [VARCHAR](50) NOT NULL,
3: [PRODUCT_CATEGORY] [NVARCHAR](128) NOT NULL,
4: [PRODUCT_NAME] [NVARCHAR](256) NOT NULL,
5: [PRODUCT_PIC] [NVARCHAR](512),
6: [PRODUCT_DESC] [NVARCHAR](800),
7: [PRODUCT_UNIT_PRICE] [DECIMAL](10,2) NOT NULL,
8: [PRODUCT_INVENTORY] [INT] NOT NULL,
9:
10: [VERSION_NO] [TIMESTAMP] NOT NULL,
11: [TRANSACTION_ID] [VARCHAR](50) NOT NULL,
12: [CREATED_BY] [NVARCHAR](256) NOT NULL,
13: [CREATED_TIME] [DATETIME] NOT NULL,
14: [LAST_UPDATED_BY] [NVARCHAR](256) NOT NULL,
15: [LAST_UPDATED_TIME] [DATETIME] NOT NULL
16:
17: CONSTRAINT [C_PRODUCT_PK] PRIMARY KEY CLUSTERED ( [PRODUCT_ID] ASC ) ON [PRIMARY]) ON [PRIMARY]
系列文章导航:
WCF版的PetShop之三:实现分布式的Membership和上下文传递
四、数据访问层设计
数据访问层定义在{Module}.DataAccess中,它完成单纯的基于数据库操作。为了便于操作,我写了一个简单的帮助类:DbHelper。DbHelper通过ADO.NET完成一些简单的操作,ExecuteReader、ExecuteNonQuery和ExecuteScalar对应DbCommand的同名方法。此外,该DbHelper与具体的数据库无关,同时支持SQL Server和Oracle。
1: using System.Collections.Generic;
2: using System.Configuration;
3: using System.Data;
4: using System.Data.Common;
5: using System.Data.OracleClient;
6: using System.Data.SqlClient;
7: namespace Artech.PetShop.Common
8: {
9: public class DbHelper
10: {
11: private DbProviderFactory _dbProviderFactory;
12: private string _connectionString;
13: private DbConnection CreateConnection()
14: {
15: DbConnection connection = this._dbProviderFactory.CreateConnection();
16: connection.ConnectionString = this._connectionString;
17: return connection;
18: }
19:
20: private void DeriveParameters(DbCommand discoveryCommand)
21: {
22: if (discoveryCommand.CommandType != CommandType.StoredProcedure)
23: {
24: return;
25: }
26:
27: if (this._dbProviderFactory is SqlClientFactory)
28: {
29: SqlCommandBuilder.DeriveParameters
30: ((SqlCommand)discoveryCommand);
31: }
32:
33: if(this._dbProviderFactory is OracleClientFactory)
34: {
35: OracleCommandBuilder.DeriveParameters
36: ((OracleCommand)discoveryCommand);
37: }
38: }
39:
40: private void AssignParameters(DbCommand command, IDictionary<string, object> parameters)
41: {
42: IDictionary<string, object> copiedParams = new Dictionary<string, object>();
43: foreach (var item in parameters)
44: {
45: copiedParams.Add(item.Key.ToLowerInvariant(), item.Value);
46: }
47: foreach (DbParameter parameter in command.Parameters)
48: {
49: if (!copiedParams.ContainsKey(parameter.ParameterName.
50: TrimStart('@').ToLowerInvariant()))
51: {
52: continue;
53: }
54:
55: parameter.Value = copiedParams[parameter.ParameterName.
56: TrimStart('@').ToLowerInvariant()];
57: }
58: }
59:
60: public DbHelper(string connectionStringName)
61: {
62: string providerName = ConfigurationManager.ConnectionStrings
63: [connectionStringName].ProviderName;
64: this._connectionString = ConfigurationManager.ConnectionStrings
65: [connectionStringName].ConnectionString;
66: this._dbProviderFactory = DbProviderFactories.GetFactory(providerName);
67: }
68:
69: public DbDataReader ExecuteReader(string procedureName, IDictionary<string, object> parameters)
70: {
71: DbConnection connection = this.CreateConnection();
72: using (DbCommand command = connection.CreateCommand())
73: {
74: command.CommandText = procedureName;
75: command.CommandType = CommandType.StoredProcedure;
76: connection.Open();
77: this.DeriveParameters(command);
78: this.AssignParameters(command, parameters);
79: return command.ExecuteReader(CommandBehavior.CloseConnection);
80: }
81: }
82:
83: public int ExecuteNonQuery(string procedureName, IDictionary<string, object> parameters)
84: {
85: using (DbConnection connection = this.CreateConnection())
86: {
87: using (DbCommand command = connection.CreateCommand())
88: {
89: command.CommandText = procedureName;
90: command.CommandType = CommandType.StoredProcedure;
91: connection.Open();
92: this.DeriveParameters(command);
93: this.AssignParameters(command, parameters);
94: return command.ExecuteNonQuery();
95: }
96: }
97: }
98:
99: public T ExecuteScalar(string procedureName, IDictionary<string, object> parameters)
100: {
101: using (DbConnection connection = this.CreateConnection())
102: {
103: using (DbCommand command = connection.CreateCommand())
104: {
105: command.CommandText = commandText;
106: command.CommandType = CommandType.StoredProcedure;
107: this.DeriveParameters(command);
108: this.AssignParameters(command, parameters);
109: connection.Open();
110: return (T)command.ExecuteScalar();
111: }
112: }
113: }
114: }
115: }
NET技术:WCF版的PetShop之二:模块中的层次划分,转载需保留来源!
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。