自同步数据访问层 - 第二部分






4.55/5 (36投票s)
在实现业务的数据访问层 SQL(命令/事务)同步数据库实体结构/模板的软件中,这是最耗时的事情,因为当您的实体结构发生更改(如数据类型、名称等)时,您需要重新实现。
引言
数据访问层不仅是软件开发挑战中最重要的一部分,也是软件系统的关键瓶颈,并且它还有增加开销和产生许多副作用的巨大潜力。然而,我们有几种方法/设计模式来实现这一层,但我们总会有一些可怕的缺陷会增加维护成本。
在实现业务的数据访问层 SQL(命令/事务)同步数据库实体结构/模板的软件中,这是最耗时的事情,因为当您的实体结构发生更改(如数据类型、名称等)时,您需要重新实现。
最重要的标准是保证您的 DBMS 事务的可靠性,但什么是可靠的事务?这是一个好问题。
ACID(原子性、一致性、隔离性、持久性)是一组属性,保证数据库事务被可靠地处理。在数据库的上下文中,对数据的单个逻辑操作称为事务。Jim Gray 在 20 世纪 70 年代末定义了这些可靠事务系统的属性。
在实现业务的数据访问层 SQL 命令/翻译同步数据库实体结构/模板的软件中,这是最耗费精力的事情,每当您更改实体结构(如数据类型、名称等)时,您都需要重写 SQL 命令。
在本文中,我将通过使用泛型、反射器和设计模式来实现一个灵活且自同步的数据访问层,这样我们就无需在开发/更新数据库中的实体之后/之前重写任何 SQL 命令。如您所知,数据库中的每个实体都是一个简单的表,每个实体实例都是该表中的一行。
Microsoft Entity Framework 引入了一些类似的解决方案,但不幸的是 EF 不支持某些 DBMS,例如 Microsoft Access,并且它不是开源解决方案。还有一个更新,在这种情况下,您可以实现自己的搜索算法或任何其他类型的特殊处理,以便在多个数据库中访问数据,例如最优搜索算法。此外,您可以用于科学目的,例如评估新方法、解决方案或数据访问算法以及冗余项。
链接 I:C# 泛型入门 链接 II:泛型简介 (C# 编程指南) 链接 III:用于 ADO.net 的 Sqlite
注意:请从上面的链接 III 下载与您的目标机器兼容的 sqlite .net 4 x82 或 64 版本,并在您的 DataAccessHandler 项目中添加引用。
背景
在上一篇文章“泛型数据访问层”中,我讨论了 .NET 中的泛型类型及其优点,但我的这个版本并不那么充分,而且这个版本也是这个想法的临时类型。但我建议您在开始自同步 DAL 之前,先看一下这个版本。
在这个版本中,我们有三个主要目标
- 一个数据访问层,具有分离的模型、上下文以及连接不同 DBMS 的模型
- 使用命令设计模式来执行实体和 ADO.NET 命令
- 防止 SQL 注入等典型威胁
链接 III: 命令设计模式
命名空间 | 描述 |
DataAccessHandler | 这是模型、上下文和数据库之间的一个中间层。不同的提供程序可以建立不同数据库之间的不同连接,另一方面,IEntity 和 IContext 位于此层,并且模型实体要使用此层,应该继承自抽象模型和上下文(IEntity , IContext )。 |
| 这个命名空间介绍了人力资源模型,用于将 HRDatabase (人力资源数据库)与数据访问处理程序连接起来。 |
AccModel | 这个命名空间介绍了会计模型,用于将 |
数据访问处理程序
DataAccessHandler
有两个重要的子命名空间(Command
和 Collection
)。Command 层是所有命令的容器,为了实现这部分,我使用了命令模式、工厂模式和泛型数据结构。Command 命名空间展示了这一点,而 Collection
是一个泛型类型的命名空间,根据下图,您可以更详细地了解 DataAccessHandler
。
图 1:命令设计模式
图 1 从右侧显示了实体命令,从左侧显示了 ADO.NET 命令。您可以认为实体命令需要 Entity
来执行,实际上这意味着 UpdateCommand
、InsertCommand
或 DeleteCommand
必须知道哪个实体应该从哪个上下文(Context
)更新、插入或删除,因此每个实体必须了解自己的上下文。这就是为什么我们有 IEntity
作为接口的原因,在上一版本中,每个 Entity
都不了解自己的上下文。图 2 说明了这一点。
图 2:IEntity 的架构
根据图 3,您可以看到 IEntity<T>
和 IContext
的关系,以获取每个 IEntity
的上下文,以及 ConnectionInfo
类来了解数据库连接信息,因此每个实体命令将了解自己的数据库连接,并且在运行时通过 IContext
进行冗余连接。
图 3:IEntity<T>
和 IContext
关系
IEntity<T>
包含 IEntityCommand<T>
以便使用 UpdateCommand<T>
、InsertCommand<T>
等。
上下文的职责
现在我们想了解 IContext
的活动和职责。IContext
在支持各种数据库方面起着至关重要的作用,因为在 IContext
中,我们可以使用 ConnectionInfo
类定义各种提供程序和连接。为了连接不同的数据库,需要不同的提供程序,并且为了实现冗余业务,我使用了 Redundancy
结构来保存多个 ConnectionInfo
类。请注意,这是一个递归属性。
下面的列表是 IContext
的职责
- 在模型中初始化实体连接信息
- 将模型的提供程序引入数据访问处理程序(
IEnginCommands
) - 处理数据库连接冗余
- 提供从实体获取模型和从模型获取实体的能力(例如,从
HRModel
如果我有Personnel
实体和Position
实体,我可以通过Personnel
实体访问Position
的列表,反之亦然)这可以使处理模型实体更加方便。
图 4:IContext 的全景图
实体命令序列图
图 5:实体命令序列图
Invoker
是一个命令工厂,我们可以创建我们的命令。在此示例中,IEntity<T>
将请求发送到 Invoker,Invoker 返回请求的命令,例如 InsertCommand<T>
给 IEntity<T>
,最后 IEntity<T>
调用命令的 Execute()
方法。其他命令也遵循相同的场景。
public virtual object Insert(T entity)
{
iEntityCommand = Invoker.Instanc.GetCommand(entity, EntityCommandType.Insert);
TransactionResult<T> result = iEntityCommand.Execute(entity);
if (result.Commit)
Collection.Add(entity);
return result.Entity.PrimaryKey.Value;
}
初始化上下文
从 Context
,我们拥有模型和数据库连接信息,因此在使用 GDA(通用数据访问)之前,我们应该初始化我们的上下文。此初始化包括创建 ConnectionInfo
数据结构并使用它来初始化 Context
的数据库连接信息。
static DataAccessHandler.ConnectionInfo HROledbConnectionInfo = new DataAccessHandler.ConnectionInfo()
{
DataSourceProvider = DataAccessHandler.Provider.Oledb,
Password = "",
Username = "",
DataSource = @"C:\Users\Homay\Desktop\GenericDataAccess\GenericDataAccessII\Database\HRDatabase.mdb",
IsValid = true
};
static DataAccessHandler.ConnectionInfo AccOledbConnectionInfo = new DataAccessHandler.ConnectionInfo()
{
DataSourceProvider = DataAccessHandler.Provider.Oledb,
Password = "",
Username = "",
DataSource = @"C:\Users\Homay\Desktop\GenericDataAccess\GenericDataAccessII\Database\AccDatabase.mdb",
IsValid = true
};
static void Main(string[] args)
{
AccModel.Context.Instanc.Initialize(AccOledbConnectionInfo);
HRModel.Context.Instanc.Initialize(HROledbConnectionInfo);
}
插入新记录
从这里我将向您展示如何向上下文添加新记录。
AccModel.Account newAccount = new AccModel.Account()
{
Name = "AccTest" ,
Description = "for test",
};
AccModel.Context.Instanc.Account.Insert(newAccount);
更新记录
从这里我将向您展示如何更新上下文中的记录。
AccModel.Account account = AccModel.Context.Instanc.Account.Collection.Select("Name", "AccTest").FirstOrDefault();
account.Name = "JustTest";
AccModel.Context.Instanc.Account.Update(account);
删除记录
从这里我将向您展示如何从上下文中删除记录。
AccModel.Account account = AccModel.Context.Instanc.Account.Collection.Select("Name", "JustTest").FirstOrDefault();
AccModel.Context.Instanc.Account.Delete(account);
SQL 注入
现在我将讨论 SQL 注入并解释我如何解决这个问题,SQL 注入是一种代码注入技术,用于攻击数据驱动的应用程序。您可以在 wiki 上阅读有关它的信息。我通过将参数传递给命令来解决这个问题。下面的代码说明了这一点。
public class InsertCommand<T> : IEntityCommand<T> where T : IEntity<T>
{
public InsertCommand(T entity, EntityCommandType entityCommandType)
: base(entity, entityCommandType)
{
}
public override TransactionResult<T> Execute(T entity)
{
TransactionResult<T> result = new TransactionResult<T>() { Commit = false, Entity = null };
string Insert = "INSERT INTO " + ((IEntity<T>)entity).EntityName;
string columns = "(";
string values = "VALUES(";
Dictionary<string, object> parameters = new Dictionary<string, object>();
Dictionary<string, object> infos = GetInfo(entity);
foreach (var item in infos) // create parameters
{
if (item.Value != null && item.Value.ToString() != "")
{
if (entity.PrimaryKeyReadOnly && item.Key == entity.PrimaryKey.Name)
continue;
columns += "[" + item.Key + "],";
values += "@" + item.Key.ToString() + ",";
parameters.Add("@"+item.Key, item.Value);
}
}
columns = columns.Remove(columns.Length - 1, 1) + ") ";
values = values.Remove(values.Length - 1, 1) + ") ";
Insert += columns + values;
IEnginCommand executeNoneQuery = Invoker.Instanc.GetCommand(EnginCommandType.ExecuteNoneQuery);
((ExecuteNoneQuery)executeNoneQuery).Execute(entity.Context.ConnectionInfo, Insert, parameters);
entity.InitializePrimaryKeyValue(new PrimaryKey(){Value=
((ExecuteNoneQuery)executeNoneQuery).Identity,Name = entity.PrimaryKey.Name});
result.Commit = true;
result.Entity = entity;
return result;
}
public override EntityCommandType Type
{
get { return EntityCommandType.Insert; }
}
}
转到上一部分
历史
- 更新日期:2013/25/11。
- 更新日期:2014/21/12