抽象数据访问层设计






4.85/5 (15投票s)
本文档旨在描述关系数据库的特定数据访问层的架构。本文档试图提供/展示一种自动化数据访问任务的形式。
引言
本文档旨在描述关系数据库的特定数据访问层的架构。本文档试图展示一种自动化数据访问任务的方式。
背景
下面,我将列出一些定义或概念,这些概念对于清晰理解本文档至关重要。
数据提供程序
在本文中,数据提供程序应理解为任何能够访问由特定数据库管理引擎存储的所有数据的组件。基于此定义,我们将找到适用于 Oracle、SQL Server、Microsoft Jet、MySQL 以及其他用于管理关系数据库的引擎类型的数据提供程序。
数据访问任务
数据访问任务将被定义为数据库中要执行的操作的抽象。考虑到这一点,用于访问所有数据的任务将封装成功执行操作所需的必要信息,并且这些信息将独立于我们使用的数据库引擎。一些数据访问任务的示例可能是:
- 在表中插入新记录
- 更新表中的现有记录
- 删除表中的记录
- 在两个表之间创建关系
数据访问任务执行器
这是另一个抽象。其功能是独立于用于连接的数据库引擎来执行已定义的操作。基本上,继承自此类的一个组件将必须实现其特定引擎的任务执行方式。数据访问任务执行器将在其实现中使用特定的数据提供程序。
脚本生成器
它也是一个抽象,它允许使用数据访问任务对象中存储的信息来生成标准的 SQL 脚本。每个任务执行器都可以实现自己的脚本生成器,或使用此层提供的默认脚本生成器。
类图

用于 SqlServer 数据提供程序的 数据访问任务执行器 的实现

使用数据访问任务
将默认使用连接字符串和设置文件中指定的数据类型来定义要使用的数据访问任务执行器。指定的数据类型应继承自 DataTaskPerformer
类,并且可能位于其他程序集中,从而使模型具有可扩展性。
QueryTask task = new QueryTask("MITABLA");
DataTable resultado = task.Execute();
示例代码 - 查询表中所有字段和所有记录。
QueryTask task = new QueryTask("MITABLA");
task.Where("Campo1 == '1'");
DataTable resultado = task.Execute();
示例代码 - 查询表中所有字段,并按记录数过滤。
QueryTask task = new QueryTask("MITABLA");
task.Select("Campo1", "Campo2", "Campo3", "Campo4");
task.Select("Campo5");
task.Where("Campo1 == '1'");
DataTable resultado = task.Execute();
示例代码 - 用于获取表中特定字段的查询。
QueryTask task = new QueryTask("MITABLA");
task.Select("Campo1", "Campo2", "Campo3", "Campo4");
task.Select("Campo5");
task.Where("Campo1 == '1'");
DataTable resultado = task.Execute();
示例代码 - 用于获取一些字段以及与表的一些连接的查询。
InsertTask newclient = new InsertTask("CLIENTE");
newclient.Set("Nombre", "Anwar Ibañez");
newclient.Set("Edad", 29);
newclient.Set("FechaDeIngreso", DateTime.Now);
newclient.Execute();
decimal newrecord = newclient.ScopeIdentity;
示例代码 - 用于在 Client
表中添加新记录的插入任务。
new DeleteTask("CLIENT", "State == 1").Execute();
示例代码 - 用于删除 Client
表中所有 State
字段等于 1
的记录。
UpdateTask updateclient = new UpdateTask("CLIENT");
updateclient.Set("State", 1);
updateclient.Set("Name", "New Name");
updateclient.Set("EntryDate", DateTime.Now);
updateclient.Where("State == 0");
updateclient.Execute();
示例代码 - 用于修改 CLIENT
表中 state
字段等于 0
的选定字段的更新任务。
在事务中执行任务
接下来将介绍如何定义一组要在同一事务中执行的任务。
QueryTask task = new QueryTask("MITABLA");
PerformerTransaction trans = task.Performer.CreateTransaction();
try
{
task.Select("Campo1", "Campo2", "Campo3", "Campo4");
task.Select("Campo5");
task.LeftJoin("TABLA2", "MITABLA.Campo1 = TABLA2.Campo1");
task.RightJoin("TABLA3", "MITABLA.Campo2 = TABLA3.Campo2");
task.Where("Campo1 == '1'");
DataTable resultado = task.Execute();
InsertTask newclient = new InsertTask("CLIENT", trans);
newclient.CurrentTransaction = trans;
newclient.Set("Nombre", "Anwar Ibañez");
newclient.Set("Edad", resultado.Rows[0]["Campo1"]);
newclient.Set("FechaDeIngreso", DateTime.Now);
newclient.Execute();
decimal newrecord = newclient.ScopeIdentity;
new DeleteTask("CLIENT", trans, "State == 1").Execute();
UpdateTask updateclient = new UpdateTask("CLIENT", trans);
updateclient.Set("State", 1);
updateclient.Set("Name", "New Name");
updateclient.Set("EntryDate", DateTime.Now);
updateclient.Where("State == 0");
updateclient.Execute();
trans.Commit();
}
catch (Exception myex)
{
trans.Rollback();
}
示例代码 - 在具有代码执行控制的事务中执行多个任务。
基于最后一个模型实现自动持久化层

嵌入式映射的静态属性

持久化的基类

实现示例

Using the Code
ClaseCompra nuevacompra = new ClaseCompra();
nuevacompra.Cliente.Nombre = "Nombre 1";
nuevacompra.Cliente.Apellido = "Apellido 1";
ClaseDetalleCompra detalle = new ClaseDetalleCompra();
detalle.Producto.Nombre = "Producto 1";
detalle.Producto.Precio = 1200;
nuevacompra.Detalle.Add(detalle);
nuevacompra.Save();
示例代码 - 在单个事务中保存新购买项(包含客户和相关详细信息)。
ClaseCompra compraexistente = new ClaseCompra();
compraexistente.Load(12);
示例代码 - 加载主键等于 12
的购买记录及其相关对象。
定义持久化类
[MapTo("CLIENTE")]
public class ClaseCliente:PersistentObject
{
private int identifier = 0;private string nombre = "";
private string apellido = ""; private string telefono = "";
private string direccion = "";
[PrimaryKey]
[MapTo("IdCliente")]
public int Identifier
{
get{ return identifier; }
set{ identifier = value;}
}
public string Nombre
{
get{ return nombre; }
set{ nombre = value;}
}
public string Apellido
{
get{ return apellido; }
set{ apellido = value;}
}
public string Telefono
{
get{ return telefono; }
set{ telefono = value;}
}
public string Direccion
{
get{ return direccion; }
set{ direccion = value;}
}
}
Client
类的示例代码 - 没有 MapTo
属性的属性与数据库中的字段同名。
未加密的动态持久化
我们称之为动态,是因为一旦类型被编译,我们就可以在外部文件中修改属性的目标。它不是加密的,因为文件没有加密。如果要求文件不能被修改,一个高级最终用户必须挂载该文件并将 MappingFile
属性的第二个参数设置为 true
。
[MappingFile("PersistenceFile.xml", false)]
public class ClaseCliente:PersistentObject
{. . .
Client
类的示例代码 - 持久化在文件中定义,并通过 MappingFile
属性引用。
持久化映射文件结构
<?xml version="1.0" encoding="utf-8" ?>
<persistencecatalog>
<typemapping assembly="Example.dll" typename="Example.ClaseCliente"
table="CLIENTE">
<mapping property="Identifier" field="IdCliente" nullsallowed="false"
isprimarykey="true" />
<mapping property="Nombre" field="Nombre" nullsallowed="true"
isprimarykey="false" />
<mapping property="Edad" ignored="true" />
</typemapping>
<typemapping assembly="Example.dll" typename="Example.ClaseCompra" table="COMPRA">
<mapping property="Identifier" field="IdCliente" nullsallowed="false"
isprimarykey="true" />
<mapping property="Nombre" field="Nombre" nullsallowed="true"
isprimarykey="false" />
<mapping property="Edad" ignored="true" />
</typemapping>
</persistencecatalog>
用于 Client
对象的未加密动态持久化的持久化定义文件的示例代码。没有属性的所有属性都使用它们在目标表中的数据库列名进行调用。没有映射的字段必须定义为 ignored
属性为 true
。
历史
这是我的第一篇文章。