WCF版的PetShop之二:模块中的层次划分

系列文章导航:

WCF版的PetShop之一:PetShop简介

WCF版的PetShop之二:模块中的层次划分

WCF版的PetShop之三:实现分布式的Membership和上下文传递


  上一篇文章主要讨论的是PetShop的模块划分,在这一篇文章中我们来讨论在一个模块中如何进行层次划分。模块划分应该是基于功能的,一个模块可以看成是服务于某项功能的所有资源的集合;层次划分侧重于关注点分离(SoC:Separation of Concern ),让某一层专注于某项单一的操作,以实现重用性、可维护性、可测试性等相应的目的。Source Code从这里下载。

  一、基本的层次结构

  我们接下来将目光聚焦到模块内部,看看每一个模块具体又有怎样的层次划分。我们将Infrastructures、Products和Orders目标展开,将会呈现出如图1所示的层次结构。

clip_image002

图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所示。

clip_image004

图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之一:PetShop简介

WCF版的PetShop之二:模块中的层次划分

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之二:模块中的层次划分,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。