上一篇打造自己的数据访问层(一)中,我们已了解了.NET对数据库操作的基本原理,并就Ado.net对象的使用提出了几点疑问:
1、如何由系统来判断数据库型。
2、如何消除这些重复代码。
而上篇中也提出了一种解决思路,对ADO.NET对象进行封装,具体应该如何实施?
1、需要一个对象,该对象用于建立内存表与物理表的之间映射关系,解决数据查询、更新操作,形成了数据映射对象,定义为DataMapping。
2、每一个映射对象只与一张物理建立映射关系,如果有多个这样的对象同时操作,如何解决?这时就需要另一个对象,用于添加映射对象集合,打包映射对象操作,形成了数据执行者,定义为DataExecutor。
想想看,只需要这两个基本对象,就可以形成简单的数据访问层了。

先实现DataMapping,它应具备如下功能。
1、需要知道物理表的信息,表名、主键集、字段集。
2、需要知道映射的是什么类型的数据库,MSSql数据库、Oracle数据库、MySql数据库或者其他类型的数据库。
3、可查询数据,即填充内存表。
4、可更新数据,并且可设置更新操作方式。
5、可加入到事务中去。
根据上述功能,可初步设计出的DataMapping类:

public class DataMapping
{
public DataMapping()
{ }

/// <summary>
/// 填充数据集
/// </summary>
public void Fill()
{

}

/// <summary>
/// 设置更新命令
/// </summary>
public void SetCommands()
{

}

/// <summary>
/// 设置数据提交事务
/// </summary>
public void SetTransaction()
{

}

/// <summary>
/// 提交数据
/// </summary>
public bool Update()
{

}

/// <summary>
/// 更新列名
/// </summary>
public string Columns
{
get
{
return columns;
}
set
{
columns = value;
}
}
private string columns = "";

/// <summary>
/// 主键名
/// </summary>
public string KeyColumns
{
get
{
return keyColumns;
}
set
{
keyColumns = value;
}
}
private string keyColumns = "";

/// <summary>
/// 表名
/// </summary>
public string TableName
{
get
{
return tableName;
}
set
{
tableName = value;
}
}
private string tableName = "";
}

再来实现DataExecutor类,它应具备的功能:
1、应该知道执行什么类型的数据库操作。
2、可以添加映射对象。
3、可以进行数据提交。
如何来知道执行的数据库类型,我们可以定义具体的执行者,比如MSSql执行者、Oracle执行者、MySql执行者。
可以初步设计出DataExecutor类

public abstract class DataExecutor
{
private IList<DataMapping> lisDataMappings = new List<DataMapping>();

/// <summary>
/// 添加数据映射对象
/// </summary>
public void AddDataMapping(DataMapping map)
{

}

/// <summary>
/// 更新数据
/// </summary>
public bool Update()
{

}
}

public class MSSqlExecutor : DataExecutor
{

}

public class OracleExecutor : DataExecutor
{

}

public class MySqlExecutor : DataExecutor
{

}

准备就绪,开始行具体设计。
先从DataMapping的Fill方法入手,看看它是如何查询数据的。

public void Fill(string sqlText, string tableName, DataSet ds)
{
IDbConnection conn = 具体的数据连接对象;
IDbDataAdapter dataAdapter = 具体的数据适配对象;
IDbCommand cmd = 具体的命令对象;
cmd.Connection = conn;
cmd.CommandText = sqlText;
dataAdapter.SelectCommand = cmd;
((DbDataAdapter)dataAdapter).Fill(ds, tableName);
}

问题出来了,这里出现了具体的对象,如何得到这些对象?
前面我们设计了MSSqlExecutor类,它已经知道具体的数据库类型,所以它也应该知道进行数据操作的具体的对象,DataMapping类是否可以引用该它,从而通过它来获取数据操作对象,因此,可以MSSqlExecutor类及DataMapping类进行修改,使DataMapping对MSSqlExecutor类产生依赖关系;这只是对MSSql数据库进行操作,现要改变数据库对象为Oracle了,DataMapping类应该也需要对OracleExecutor类产生依赖关系。
因此,这里可以设计一个接口,用于获取具体对象:

/// <summary>
/// 映射执行接口
/// </summary>
public interface IMappingExecute
{
/// <summary>
/// 获取连接对象
/// </summary>
IDbConnection GetConn();

/// <summary>
/// 获取数据适配器
/// </summary>
IDbDataAdapter GetDataAdapter();

/// <summary>
/// 获取命令对象
/// </summary>
IDbCommand GetCommand();

/// <summary>
/// 获取命令参数
/// </summary>
IDbDataParameter GetDataParameter(string col);

/// <summary>
/// 获取命令参数
/// 数据库之间的命令参类是不一样的
/// MMSql是“@” + 列名,Oracle是 “:” + 列名,MySql是 “?” + 列名
/// </summary>
string GetSourceColumn(string col);
}

改造后的MSSqlExecutor类为:

public class MSSqlExecutor : DataExecutor, IMappingExecute
{
}

改造后的DataMapping类为:

public class DataMapping
{
private IDbConnection conn = null;
private IDbDataAdapter dataAdapter = null;

/// <summary>
/// 映射执行对象
/// </summary>
public IMappingExecute ExecuteObject
{
set
{
executeObj = value;
conn = executeObj.GetConn();
}
}
private IMappingExecute executeObj;

/// <summary>
/// 填充数据集
/// 参数:查询语句
/// 参数:内存表名
/// </summary>
public void Fill(string sqlText, string tableName, DataSet ds)
{
dataAdapter = executeObj.GetDataAdapter();
IDbCommand cmd = executeObj.GetCommand();
cmd.Connection = conn;
cmd.CommandText = sqlText;
dataAdapter.SelectCommand = cmd;
((DbDataAdapter)dataAdapter).Fill(ds, tableName);
}
}

到此为止,查询功能算是完成了,接下来该实现更新功能了,从SetCommands入手,以新增操作为例:

/// <summary>
/// 设置更新命令
/// </summary>
public void SetCommands(DataCommandType commandType, DataSet ds)
{
if ((commandType & DataCommandType.Insert) == DataCommandType.Insert)
{
CreateInsertCommand(ds);
}

if ((commandType & DataCommandType.Update) == DataCommandType.Update)
{
CreateUpdateCommand(ds);
}

if ((commandType & DataCommandType.Delete) == DataCommandType.Delete)
{
CreateDeleteCommand(ds);
}
}

/// <summary>
/// 生成新增命令及SQL语句
/// </summary>
private void CreateInsertCommand(DataSet ds)
{
IList<string> lisColumns = GetColumns(ds);
StringBuilder sbCol = new StringBuilder();
StringBuilder sbVal = new StringBuilder();
foreach (string col in lisColumns)
{
sbCol.AppendFormat(", {0}", col);
sbVal.AppendFormat(", {0}", executeObj.GetSourceColumn(col));
}

sbCol.Remove(0, 2);
sbVal.Remove(0, 2);
string sqlText = string.Format("INSERT INTO {0} ({1}) VALUES ({2})", tableName, sbCol.ToString(), sbVal.ToString());
IDbCommand cmd = executeObj.GetCommand();
cmd.Connection = conn;
cmd.CommandText = sqlText;
SetCommandParams(cmd, lisColumns);
dataAdapter.InsertCommand = cmd;
}

/// <summary>
/// 获取列字段集
/// </summary>
private IList<string> GetColumns(DataSet ds)
{
IList<string> lisColumns = new List<string>();
if (columns != "*")
{
string[] sltCol = columns.Split(',');
foreach (string col in sltCol)
{
lisColumns.Add(col.Trim());
}
}
else
{
DataTable dt = ds.Tables[tableName];
foreach (DataColumn dc in dt.Columns)
{
lisColumns.Add(dc.ColumnName);
}
}

return lisColumns;
}

更新操作非常简单,就是在上一篇的数据操作原理的基础上动态生成了查询语句及参数绑定,不多做解释。 
其中DataCommandType为自定义枚举类型:

/// <summary>
/// 数据操作命令类型
/// </summary>
public enum DataCommandType
{
/// <summary>
/// 新增
/// </summary>
Insert = 1,

/// <summary>
/// 修改
/// </summary>
Update = 2,

/// <summary>
/// 删除
/// </summary>
Delete = 4
}

更新完后进行数据提交:

/// <summary>
/// 提交数据
/// </summary>
public bool Update(DataSet ds)
{
return ((DbDataAdapter)dataAdapter).Update(ds, tableName) > 0;
}

至此,数据更新操作也已经完成,最后再看看数据执行者是如何进行批量提交。
这里产生的第一个问题是,什么时候数据执行者会人将映射对象加入到集合中来,其中一种方法是在DataMapping设置更新的时候自己加入到集合去。
因此, 映射执行接口得多添加一个方法,用于新增映射对象:

/// <summary>
/// 添加数据映射对象
/// </summary>
void AddDataMapping(DataMapping map);

修改SetCommand方法:

/// <summary>
/// 设置更新命令
/// </summary>
public void SetCommands(DataCommandType commandType, DataSet ds)
{
//设置更新事件时添加映射对象
executeObj.AddDataMapping(this);
}

现在执行者中已经存在了,可以进行最后的数据提交:

/// <summary>
/// 更新数据
/// </summary>
public bool Update(DataSet ds)
{
using (conn)
{
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}

IDbTransaction transaction = conn.BeginTransaction(IsolationLevel.ReadCommitted);
foreach (DataMapping map in lisDataMappings)
{
map.SetTransaction(transaction);
}

try
{
foreach (DataMapping map in lisDataMappings)
{
map.Update(ds);
}

transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
throw new System.Exception(ex.Message);
}
}

return true;
}

//DataMapping类设置事务
/// <summary>
/// 设置数据提交事务
/// </summary>
public void SetTransaction(IDbTransaction transaction)
{
if (dataAdapter.InsertCommand != null)
{
dataAdapter.InsertCommand.Transaction = transaction;
}

if (dataAdapter.UpdateCommand != null)
{
dataAdapter.UpdateCommand.Transaction = transaction;
}

if (dataAdapter.DeleteCommand != null)
{
dataAdapter.DeleteCommand.Transaction = transaction;
}
}

到些为止,我们自己的数据访问层功能已基本完成,但是,我们要如何对其进行调用,现在的方式真能方便的让我们进行调用吗?答案是否定的。
暂且至此为止,下一篇我们再进行最后的收尾工作,并最终打造成符合自己需求的数据访问层。

转载于:https://www.cnblogs.com/FlySoul/archive/2011/05/04/2036953.html

打造自己的数据访问层(二)相关推荐

  1. 打造自己的数据访问层(三)

    上一篇打造自己的数据访问层(二)中,我们已具体实现了数据访问层对应的功能,该进行收尾工作了,先来看段代码,试试上一篇实现的功能: View Code string sqlText = "SE ...

  2. petshop4.0 详解之二(数据访问层之数据库访问设计)

    在系列一中,我从整体上分析了PetShop的架构设计,并提及了分层的概念.从本部分开始,我将依次对各层进行代码级的分析,以求获得更加细致而深入的理解.在PetShop 4.0中,由于引入了ASP.Ne ...

  3. 第六章节 三层架构(二. 模型层与数据访问层)

    一.模型层 1.由于三层之间存在数据交互,所以需要中间介质那就是"模型层",模型层包括与数据库表相对应的实体类,"实体类就相当于数据库中的数据表,实体类里的属性就相当于数 ...

  4. NHibernate3.2+Asp.net MVC3+Extjs 4.0.2项目实践(二): NHibernate数据访问层实现

    关于NHibernate的ORM映射可以通过Hbm映射文件来完成,代码生成工具使得这一步骤变得简化:而NHibernate3.2版本集成Mapping-By-Code(代码映射),不同于其他映射方式, ...

  5. 企业级应用架构(二)三层架构之数据访问层的封装与抽象

    在上一篇我们知道,要解除BLL对DAL的依赖,我们就必须抽象出DAL层的接口,同时基于DAL的数据访问技术很多,如EF,ADO.NET,LINQ TO SQL,因此,我们的数据访问层必须对这些技术提供 ...

  6. 数据访问层之数据库访问设计(转)

    在PetShop中,系统需要处理的数据库对象分为两类:一是数据实体,对应数据库中相应的数据表.它们没有行为,仅用于表现对象的数据.这些实体类都被放到Model程序集中,例如数据表Order对应的实体类 ...

  7. CYQ.Data 轻量数据访问层(一) 概述

    在很久很久以前.2007年底,我曾发布过CYQ.Data.DLL,那时的学术氛围很浓,评论的也比较重 在那里,我曾做过一些简介与使用方法的帮助 在这个系列中,我将一步一步开源并讲解实现的过程,由于文章 ...

  8. Scott Mitchell 的ASP.NET 2.0数据教程之一: 创建一个数据访问层

    原文 | 下载本教程中的编码例子 | 下载本教程的英文PDF版 导言 作为web开发人员,我们的生活围绕着数据操作.我们建立数据库来存储数据,写编码来访问和修改数据,设计网页来采集和汇总数据.本文是研 ...

  9. [wayfarer]PetShop数据访问层之数据库访问设计

    原文地址: http://www.cnblogs.com/wayfarer/archive/2006/04/21/381315.html <解剖PetShop>系列之二 二.PetShop ...

最新文章

  1. linux中光标向上调一行命令,Linux vi 中移动光标 命令
  2. mysql解析运行时间_分析 MySQL 语句运行时间
  3. Windows 2003 AD升级至Windows 2012 AD之DHCP服务器迁移
  4. 功能分支重新设置后,Git推送被拒绝
  5. 基于visual c++之windows核心编程代码分析(64)现有的exe文件中添加自己的代码
  6. 取一列_excel工作案例:如何快速实现一列变两列?
  7. NYOJ 2 括号配对问题
  8. apache ab测试与centos系统优化
  9. 三维视觉基础之世界坐标系、相机坐标系、图像坐标系和像素坐标系之间的转换关系
  10. 使用AIDL挂断电话
  11. 基于大数据的房价数据可视化分析预测系统
  12. C语言——快速取以2为底x的对数
  13. 若依移动版开发对接企业微信网页授权登陆
  14. 关于刷微信投票的js代码
  15. 乐理基础-和弦、调式
  16. layui 如何取得select下拉框选中的值
  17. import express from 'express'; ^^^^^^^ SyntaxError: Unexpected identifier at Module._com
  18. python turtle画动物_用python画简单的动物
  19. 汽车租赁小程序源码 上门取车
  20. mysql数据库搭建动态网站_数据库和动态网页怎么建立联系

热门文章

  1. 标准C程序设计七---12
  2. git使用(一)----git安装
  3. Android 聊天软件客户端
  4. java 消息机制 ActiveMQ入门实例
  5. 浅析CSS——元素重叠及position定位的z-index顺序
  6. Perl Learning (5) —— 输入与输出
  7. (天国之扉文章抢救) 1/10/2003 总结?总结!
  8. 全网最新 Skywalking 6.1.0部署进k8s 包含springcloud测试用例
  9. Live回顾 | 松鼠AI智适应教育首席科学家崔炜:人工智能如何变革教育产业
  10. 【SQL】使用调用层接口