在SQL Server中编写通用数据访问方法
作者:doorsir 来源:赛迪网技术社区 发布时间:2009-01-20 11:35:00
引言
在负责咨询工作的过去 6 年中,我曾多次听说关于数据访问和操作方面的问题,它时刻困扰着用户:“如何编写应用程序,以便只需对其进行很少的改动或不进行改动即可使用数据库服务器 x、y 和 z?”由于知道数据访问层仍然是现代应用程序的最关键部分,并且通常是经验不足的开发人员的头号敌人,因此我的第一反应始终是:根本办不到!
面对着人们惶惶不安的面孔以及“使用 Microsoft 在 ADO 中提供的通用数据访问方法如何?”这样的问题,我决定针对此问题提供更详细的说明以及建议的解决方案。
问题在于,如果应用程序是较小的原型,或者如果并发用户较少并且数据访问逻辑比较简单,那么即使您选择下面这些最简单的方法,也不会遇到任何问题:使用 RAD 工具(如 Data Environment in Microsoft? Visual Basic? 6.0),或某些“一揽子”解决方案(如 ActiveX? Data Control 和其他第三方组件),这些解决方案通常会隐藏应用程序与特定数据源之间进行的复杂交互。然而,当用户数量增加使得必须解决并发操作问题时,由于频繁使用动态记录集、服务器端光标以及不必要的锁定策略,导致出现许多性能问题。为达到用户目标而必须对系统所做的设计和代码更改将花费您大量的时间,因为您从开始时就没有考虑过这一问题。
使用通用数据访问方法
在将 ADO 可靠地并入 MDAC(Microsoft Data Access Components 2.1 版)后,Microsoft 掀起了通用数据访问的使用高潮。其主导思想是向开发人员展示,通过使用简单的对象模型(“连接”、“命令”和“记录集”),可以编写出能够与各种不同的数据源(无论是关系数据源还是非关系数据源)连接的应用程序。文档(以及当时的大多数文章和示例)中通常未曾提及的是,即使使用相同的数据访问技术,各种数据源的可编程性和特征也千差万别。
其结果是,在需要从多个数据源获取数据的应用程序中,最简单的方法是使用所有数据源所提供的功能的“共同点”,但因此会失去使用数据源特定选项的好处,即为访问和操作各种 RDBMS 中的信息提供最佳方法。
我对该方法始终存在的怀疑是,经过与我的客户进行更详细的分析后,我们通常一致认为与应用程序中处理显示和业务逻辑的其他部分相比,与数据源进行交互的只是应用程序很小的一部分。通过进行精心的模块化设计,可以将 RDBMS 特定代码隔离在一些容易互换的模块中,从而避免对数据访问使用“通用”方法。然而,我们可以使用非常特定的数据访问代码(根据数据源的不同,使用存储过程、命令批处理和其他特性),而不触及其他大多数应用程序代码。这总是提醒大家:正确的设计是编写可移植的有效代码的关键。
ADO.NET 将一些重要的变化引入到数据访问编码领域,如专用 .NET 数据提供程序这样的概念。使用特定的提供程序,可以绕过为数众多但有时没必要的一系列软件接口和服务(它们是 OLE DB 和 ODBC 层在数据访问代码与数据库服务器之间插入的内容),从而以最佳方式连接到数据源。但每个数据源仍然存在不同的特征和特性(具有不同的 SQL Dialect),且编写高效的应用程序仍然必须使用这些特定特征而不是“共同点”。从可移植性观点看来,托管和非托管的数据访问技术仍然非常类似。
除“利用数据源的唯一特征”外,编写良好数据访问层所必需的其他规则对每个数据源通常都是相同的:
在可能的情况下使用连接池机制。
节约使用数据库服务器的有限资源。
注意网络的往返。
在适当的情况下,增强执行计划的重复使用率并避免重复编译。
使用适当的锁定模型管理并发性。
从我使用模块化设计方法的个人经验来看,整个应用程序中专用于处理特定数据源的代码量不会超过总量的 10%。显而易见,这比仅仅更改配置文件中的连接字符串更复杂,但我认为,这样做会获得性能收益,因此这是一个可接受的折衷办法。
使用基本接口
此处的目标是使用抽象,并将特定于特殊数据源的代码封装在类层中,从而使应用程序的其他部分独立于后端数据库服务器或免受其影响。
.NET Framework 的面向对象这一特性将在该过程中为我们提供帮助,使我们能够选择要使用的抽象级别。选项之一是使用每个 .NET 数据提供程序都必须实现的基本接口(IDbConnection、IDbCommand、IDataReader 等)。另一个选项是创建一组类(数据访问层),用于管理应用程序的所有数据访问逻辑(例如,使用 CRUD 范例)。为检查这两种可能性,我们首先从基于 Northwind 数据库的订单输入应用程序示例入手,然后插入和检索不同数据源中的信息。
数据提供程序基本接口标识应用程序与数据源进行交互通常所需的典型行为:
定义连接字符串。
打开和关闭与数据源的物理连接。
定义命令和相关参数。
执行可以创建的不同种类的命令。
返回一组数据。
返回标量值。
对数据执行操作但不返回任何内容。
对返回的数据集提供只向前型访问和只读型访问。
定义使数据集与数据源(数据适配器)的内容保持同步所需的一组操作。
但事实上,如果将检索、插入、更新和删除不同数据源(使用不同的数据提供程序)中的信息所需的各种操作封装在数据访问层中,并且只公开基本接口的成员,则可以实现第一级抽象-至少从数据提供程序的角度来看是这样。让我们看一看以下演示该设计思想的代码:
using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Data.OleDb;
using System.Data.OracleClient;
namespace DAL
{
public enum DatabaseType
{
Access,
SQLServer,
Oracle
// 任何其他数据源类型
}
public enum ParameterType
{
Integer,
Char,
VarChar
// 定义公用参数类型集
}
public class DataFactory
{
private DataFactory(){}
public static IDbConnection CreateConnection
(string ConnectionString,
DatabaseType dbtype)
{
IDbConnection cnn;
switch(dbtype)
{
case DatabaseType.Access:
cnn = new OleDbConnection
(ConnectionString);
break;
case DatabaseType.SQLServer:
cnn = new SqlConnection
(ConnectionString);
break;
case DatabaseType.Oracle:
cnn = new OracleConnection
(ConnectionString);
break;
default:
cnn = new SqlConnection
(ConnectionString);
break;
}
return cnn;
}
public static IDbCommand CreateCommand
(string CommandText, DatabaseType dbtype,
IDbConnection cnn)
{
IDbCommand cmd;
switch(dbtype)
{
case DatabaseType.Access:
cmd = new OleDbCommand
(CommandText,
(OleDbConnection)cnn);
break;
case DatabaseType.SQLServer:
cmd = new SqlCommand
(CommandText,
(SqlConnection)cnn);
break;
case DatabaseType.Oracle:
cmd = new OracleCommand
(CommandText,
(OracleConnection)cnn);
break;
default:
cmd = new SqlCommand
(CommandText,
(SqlConnection)cnn);
break;
}
return cmd;
}
public static DbDataAdapter CreateAdapter
(IDbCommand cmd, DatabaseType dbtype)
{
DbDataAdapter da;
switch(dbtype)
{
case DatabaseType.Access:
da = new OleDbDataAdapter
((OleDbCommand)cmd);
break;
case DatabaseType.SQLServer:
da = new SqlDataAdapter
((SqlCommand)cmd);
break;
case DatabaseType.Oracle:
da = new OracleDataAdapter
((OracleCommand)cmd);
break;
default:
da = new SqlDataAdapter
((SqlCommand)cmd);
break;
}
return da;
}
}
}


猜你喜欢
- 应用一:有时候我们想把一个 list 或者 dict 传递给 javascript,处理后显示到网页上,比如要用 js 进行可视化的数据。请
- --利用T-SQL语句,实现数据库的备份与还原的功能 ----体现了SQL Server中的四个知识点: ----1. 获取SQL Serv
- 界面设计页面引用了youzan组件进行设计,包括icon,button,tag,toast以及布局github地址:https://gith
- 简介枚举是与多个唯一常量绑定的一组符号因为枚举表示的是常量,建议枚举成员名用大写IntEnum 便于进行系统交互初试from enum im
- 之前给大家介绍过单机当前进程的滑动窗口限流 , 这一个是使用go redis list结构实现的滑动窗口限流 , 原理都一样 , 但是支持分
- 话不多说,请看代码:SQLServer Procedure Pagination_basic:ALTER PROCEDURE [qianch
- 前言使用pandas进行数据分析的时候,我们经常需要对DataFrame的行或者列进行索引。使用pandas进行索引的方法主要有三种:直接使
- 概述Alwayson相对于数据库镜像最大的优势就是可读副本,带来可读副本的同时还添加了一个新的功能就是配置只读路由实现读写分离;当然这里的读
- 无限循环如果条件判断语句永远为 true,循环将会无限的执行下去。如下实例#!/usr/bin/python# -*- coding: UT
- 本博文源于绘图基础,主要讲解如何用python的plot绘制气温的折线图。先讲解plot参数如何使用后给出一个气温折线图样例绘制使用plot
- 1. 如何停止任务?我们可以通过 asyncio.Task 对象上的 cancel() 方法取消任务。如果任务被取消,cancel() 方法
- 引言最近在做个表情包的小程序,涉及到表情包搜索功能。我们上传表情包的时候,只有一张图,怎么搜索?这个时候我们想到就是将表情包的文字提取出来,
- PIL vs PillowPIL: Python Imaging Library,是python的图像处理库。由于PIL不兼容setupto
- 本文实例讲述了GO语言Defer用法。分享给大家供大家参考。具体分析如下:defer:调用一个被 defer 的函数时在函数刚要返回之前延迟
- 如下图所示的文件,我们按文件名后缀对文件进行分类使用os和shutil首先导入对应的模块,将目标文件夹和到分类到的文件夹定义import o
- 前言最近做了几个简单的爬虫python程序,于是就想做个窗口看看效果。首先是,窗口的话,以前没怎么接触过,就先考虑用Qt制作简单的ui。这里
- 例如:将日期格式为2009-6-8的转换为2009-06-08,给小于10的数字补上一个0方法一:year(now)
- 将纸的材质融为设计元素现已成为当今网页设计最热门最流行的设计趋势之一。网页设计师可以使用纸屑、硬纸板纹理材质、笔记本和记事薄构成有趣而复杂的
- 引言现在已经产生了一种新职业:Prompt Engineer(提示指令工程师),可见 Prompt 是多么重要,且编写不易。ChatGPT的
- 在一般问题的优化中,最速下降法和共轭梯度法都是非常有用的经典方法,但最速下降法往往以”之”字形下降,速度较慢,不能很快的达到最优值,共轭梯度