前段时间提到使用DAAB模块引用到自己的系统中遇到的困惑:http://dragonpro.cnblogs.com/archive/2005/10/20/258486.html
后来经过我的反复思索和试验,找到了一种折中的方式,既尽量减少了重复代码屏蔽了数据库的异构问题,又可以较合理体现分层体系。现在抽空把这个架构分享出来,给大家多多少少一点参考,也希望多批评指正。
目的:
- 轻松切换数据库平台,在不修改业务逻辑的前提下切换数据库
- 尽量减少重复代码量,能抽象出来的抽象出来
- 降低维护成本
按照这三方面来考虑,参考了这么几种方式:
1. 单独建立DAL接口,在数据提供层分别实现这些接口,在应用层调用这些接口方法,另外还要工厂方法来实时装配这些实现,这适合在一些较小型的应用,比如.NET Petshop。优点是规范,性能好,缺点是维护较麻烦。
2. 在应用层提供抽象类的抽象方法,然后分别在数据层实现这些方法,在抽象类里使用Singleton模式使用静态方式加载实现层,这可以是一些较大型的应用,比如CommunityServer。优点是规范,性能好,缺点是代码量大,迁移维护较麻烦。
3. 还有就是微软提供的Entriprise Library DAAB模块,用次模块声称可以屏蔽调数据库的异构性。优点是可以尽量减少代码量,缺点是并不能完全屏蔽数据库异构,并没有声称的那么完美,比如Oracle多数据集的返回和其他一些特性,支持的并不好。
4. 用Hibernate类似的对象持久化技术。优点是可以可以规范对象和方便的持久层配置,缺点是不太灵活,也不适合在已有的系统中实现。
上面几种方式都是比较流行的做法,但有缺点和优点并存,作为追求完美的我们,想想能不能做到既可以很好的规范和性能又可以尽量减少代码维护的成本呢,我试着这么做了一下,其实就是折中了一下这几种方案。
如图,仍然基于上面的分层方法,只是在抽象类里使用了DAAB模块,这样大部分返回单数据集或执行单条语句或执行单独的存储过程的方法都可以在这个抽象模块里使用DAAB模块实现,只有遇到DAAB模块没法解决的时候再在实现层实现。这种情况适合需要同时支持多数据库的系统,并且数据库的数据结构和存储过程名和参数要一致,可以在做完一个数据库后再迁移到另外的数据库系统,迁移工具和方法可以参考这些文章:
http://dragonpro.cnblogs.com/category/27102.html
示例代码:
- 抽象层代码
using System;
using System.Data;
using Microsoft.Practices.EnterpriseLibrary.Data;
namespace DataProvider
{
/**//// <summary>
/// 数据访问提供者示例,一般为抽象函数,前面加“I”表示这是一个接口,继承的子类不加"I"
/// 如果不需要分别实现Oracle和SqlServer的则不用申明为抽象类了,示例见LSATableDataProvider.cs
/// </summary>
/// <author>longfei</author>
public abstract class IExampleDataProvider
{
/**//*******************************************************************
*
* 以下为每个抽象类所必须实现的方法
*
* *****************************************************************/
/**//// <summary>
/// 1。申明一个固定变量表示继承此抽象类的子类的名称,此名称自己定义,
/// 无论Sqlserver还是Oracle数据库,继承的子类都需要使用这个类名称。
/// </summary>
protected static readonly string CommonDataProviderName = "ExpmpleDataProvider";
/**//// <summary>
/// 2。申明一个静态的本实例的变量,保存本类的唯一对象。
/// </summary>
private static IExampleDataProvider _defaultInstance = null;
/**//// <summary>
/// 3。静态构造函数,意在通过配置构造自己的子类
/// </summary>
static IExampleDataProvider()
{
_defaultInstance = DataProviderHelper.GetInstanceByClassName(CommonDataProviderName) as IExampleDataProvider;
}
/**//// <summary>
/// 4。建立构造实例的方法,使用的时候调用Instabce即可
/// </summary>
/// <returns></returns>
public static IExampleDataProvider Instance()
{
return _defaultInstance;
}
/**//*******************************************************************
*
* 以下为具体方法的示例
*
* *****************************************************************/
/**//// <summary>
/// 其他。可以是抽象函数,如果是抽象函数那么在SqlServer和Oracle分别要实现此方法
/// </summary>
public abstract string ExampleMethod();
/**//// <summary>
/// 如果不需要分别实现的可以写为具体的方法,
/// 而不是虚方法,这样在子类就不用分别实现
/// </summary>
/// <param name="inValue"></param>
public string ExampleMethod(string inValue)
{
return "这是抽象类本身返回的值";
}
}//calss
}//namespace
using System;
using System.Data;
using System.Reflection;
using System.Configuration;
namespace DataProvider
{
/**//// <summary>
/// 提供数据提供者的公用方法
/// </summary>
public sealed class DataProviderHelper
{
/**//// <summary>
/// 初始化变量
/// </summary>
static DataProviderHelper()
{
}
static string dataProvider = ReadSystem("DefaultProviderAssembly");
/**//// <summary>
/// 根据传入的类名返回类的实例
/// </summary>
/// <param name="className">传入的类名</param>
/// <returns>返回相应的实例对象</returns>
public static object GetInstanceByClassName(string className)
{
string clsName = dataProvider + "." + className;
object obj = Assembly.Load(dataProvider).CreateInstance(clsName);
return obj;
}
/**//// <summary>
/// 获取用户数据库配置的实例名称
/// </summary>
public static string DefaultDatabaseInstanceName
{
get{
return ReadSystem("DefaultDatabaseInstance");
}
}
/**//// <summary>
/// 获取系统变量配置信息
/// </summary>
/// <param name="SystemValue"></param>
/// <returns></returns>
static public string ReadSystem(string SystemValue)
{
return System.Configuration.ConfigurationSettings.AppSettings[SystemValue];
}
}//class
}//namespace
- 实现层代码
using System;
using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Data;
using System.Data.OracleClient;
namespace DataProviderOracle
{
/**//// <summary>
/// ExpmpleDataProvider 的Oracle实现
/// </summary>
public class ExpmpleDataProvider : IExampleDataProvider
{
public ExpmpleDataProvider()
{
}
/**//// <summary>
/// Oracle实现方法
/// </summary>
/// <returns></returns>
public override string ExampleMethod()
{
return "这是ExampleMethod的Oracle实现";
}
}//class
}//namespace