如何应对数据库更换(三层架构+抽象工厂+反射+配置文件)

引言

在项目开发中,数据库的更换是需要考虑的问题,不同的数据库有着不同的访问方式,不同的SQL语法。恐怕最让人头疼的是项目开发完了,用户提出了更换数据库的要求,修改并不是一件容易的事儿。那么如何应对修改,本文将通过三层架构+抽象工厂+反射+配置文件结合实例讲述数据库的更换。
概念
三层架构

三层架构即在软件架构中,将整个业务应用划分为:界面层(User Interface Layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data Access Layer)。

具体内容可以参考我的另一篇关于三层架构的文章:http://blog.csdn.net/u013201439/article/details/50704816
抽象工厂

抽象工厂(Abstract Factory)是设计模式中的一种:

    它提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

这里写图片描述

具体内容参见【设计模式】工厂三模式(创建型):http://blog.csdn.net/u013201439/article/details/50488009
反射

    反射提供了描述程序集、模块和类型的对象( Type 类型)。 可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了特性,可以利用反射来访问它们。

简单的来讲,就是传入所需类的字符串,然后返回这个类的实例。
配置文件

    配置文件为开发人员和管理员提供了对应用程序运行方式的控制权和灵活性。配置文件是可以按需要更改的 XML文件。管理员能够控制应用程序可以访问哪些受保护的资源,应用程序将使用哪些版本的程序集,以及远程应用程序和对象位于何处。开发人员可以将设置置于配置文件中,从而没有必要在每次设置更改时重新编译应用程序。

简单理解就是将软件中容易修改的内容保存到配置文件中,需要修改时,只需要修改配置文件的内容,而无需修改程序本身,重新编译。
实现

为了加深理解,将上述这些应用到实例项目中,来实例分析,以机房收费系统为例。

在项目开始之前,先通过一张包图理清各层之间的关系。

这里写图片描述

实例为实现显示用户信息功能,上图中还添加了“外观模式”,不是本文讨论的重点,大家忽略就可以了。
实体层:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JFEntity
{
    public class UserInfo
    {

        public int userID { get; set; }//用户账号
        public string userName { get; set; }//用户姓名
        public string userSex { get; set; }//用户性别
        public string userPassword { get; set; }//用户密码
        public string userLevel { get; set; }//用户级别
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

实体层对应数据库JFCharge的T_UserInfo表。
IDAL(接口层):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JFIDAL
{
    public interface IUser
    {
        List<JFEntity.UserInfo> ShowUserInfo();//显示用户信息
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

DAL:

DAL(数据访问层),这一层实现了IDAL层的IUser接口。SqlServerUser是表示是对Sql Server 数据库操作的User类,同样的我们可以添加AccessUser、MySqlUser、OracleUser类来实现对不同数据库的操作。它们都实现IUser接口。

下面还涉及到了SqlHelper,泛型转换的使用,在后面的文章中会逐一介绍。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Net;

namespace JFDAL
{
    public class SqlServerUser : JFIDAL.IUser
    {
        #region ShowUserInfo 显示用户信息
        /// <summary>
        /// 显示用户信息
        /// </summary>
        /// <returns></returns>
        public List<JFEntity.UserInfo> ShowUserInfo()
        {
            string sql = @"SELECT userID,userName,userSex,userLevel FROM T_User";//sql语句查询用户信息
            DataSet ds = SqlHelper.ExecuteDataset(SqlHelper.connectionString, CommandType.Text, sql, null);
            DataTable dt = ds.Tables[0];
            List<JFEntity.UserInfo> list = new List<JFEntity.UserInfo>();
            list = ConvertHelp.ToList<JFEntity.UserInfo>(dt);//将datatable转换为泛型
            return list;返回泛型
        }
        #endregion
    }
}

    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

Factory:

工厂层,从名字上就能知道工厂是生产产品的,这里就可以理解成为我们实例化所需要的类,提供对象。

按照常规思维,因为有不同数据库访问类(SqlServerUser,AccessUser等),所以涉及到选择语句,会使用switch。而我们现在要考虑的是可不可以不在程序里写明“如果是Sql Server就去实例化SqlServerUser类,如果是Access就去实例化AccessUser类”,而是根据字符串db的值去某个地方找应该实例化的类。这样,我们就不需要使用switch语句了。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Configuration;
//引入反射


namespace JFFactory
{
    public class UserManagerFactory
    {
        private static readonly string AssemblyName = "JFDAL";//获取程序集名称
        private static readonly string db = ConfigurationManager.AppSettings["DB"];
        //读取配置文件,选择所需的数据库


        public static JFIDAL.IUser creatUser()
        {
            string className = AssemblyName + "." + db + "User";
            return (JFIDAL.IUser)Assembly.Load(AssemblyName).CreateInstance(className);
            //通过反射实例化数据库相关类
        }
    }
}

    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

配置文件内容:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
    <appSettings>
        <add key="DB" value="SqlServer"/>
        <add key ="connStr" value ="Data Source=127.0.0.1;Initial Catalog=JFCharge;Persist Security Info=True;User ID=JFCharge;Password=123456"/>
    </appSettings>
</configuration>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

这样程序运行的时候,就会从配置文件读取DB的值,在工厂中完成字符串的拼接,通过反射得到我们所需要的对象。当需要更换数据库时,只要在配置文件中修改DB的值即可。
BLL:

业务逻辑层,这里体现了面向对象三大特征之一的多态。现在如果要更换数据的话,由于多态的关系,IUser接口的对象iuser事先根本不知道是在访问哪一个数据库,但是可以在运行时完成工作,这就是业务逻辑和数据访问的解耦。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JFBLL
{
    public class UserManager
    {
        #region ShowUserInfo 显示用户信息
        /// <summary>
        /// 显示用户信息
        /// </summary>
        /// <returns></returns>
        public List<JFEntity.UserInfo> ShowUserInfo()
        {
            JFIDAL.IUser iuser = JFFactory.UserManagerFactory.creatUser();//得到实际的数据库访问实例
            return iuser.ShowUserInfo();//调用ShowUserInfo方法
        }
        #endregion
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

小结

上面就是通过反射+抽象工厂模式解决数据库访问时的可维护、可拓展的问题。
---------------------
作者:Bboy-AJ-任杰
来源:CSDN
原文:https://blog.csdn.net/u013201439/article/details/51161230
版权声明:本文为博主原创文章,转载请附上博文链接!

posted @ 2019-05-31 20:56  天涯海角路  阅读(164)  评论(0)    收藏  举报