ADO.NET 扩展

728 篇文章 1 订阅
86 篇文章 0 订阅

ADO.NET 扩展

 

ADO.NET 成功地提供了一组基类和接口,为通过其他类和接口访问关系型数据库提供了一种选择,因此,从 F# 中访问大多数关系型数据库不用太多的努力。我们已经讨论了大部分这样的类,或者至少这些类实现了它们打算提供的功能。表 9-2 汇总了其中的关键类。

表 9-2 ADO.NET 中的关键类

 

描述

System.Data.Common

.DbConnection

这个类表示对关系型数据库特定实例的连接,使用从它派生的类,指定想对哪个数据库执行执行查询。

System.Data.Common

.DbCommand

从这个基类派生的类,用于配置想对数据库执行哪种查询,是 SQL 查询,还是存储过程。

System.Data.Common

.DbParameter

这个类表示查询的参数。通常,参数化查询有利于在关系数据库中重用,使执行更有效。

System.Data.Common

.DbDataReader

从这个类派生的类,能够以线性方式访问查询的结果,可以快速访问结果。

System.Data.Common

.DbDataAdapter

这个类用于从关系型数据库的数据充填数据集(DataSet)类。

System.Data.DataSet

这个类提供了数据库中内存中的表示,可以包含表和表之间的关系,不像本表中的其他类,这个类是具体的,可以直接使用。

 

在表 9-2 中的类,除了 System.Data.DataSet 类以外,都是抽象的,因此,必须使用类的具体实现。例如,这个示例演示了如何创建 System.Data.SqlClient.SqlConnection 类的实例,它是System.Data.Common.DbConnection 类的实现,它用于访问 SQL Server 数据库:

 

Use connection = newSqlConnection(connectionString)

 

如果想访问Oracle 数据库,只要把SqlConnection 类简单地替换成 OracleConnection。表 9-3 汇总了最流行的库,经及实现这些类的命名空间。这个表并不完整,因为提供程序的范围很广。

 

表 9-3  .NET 版本的数据库提供程序

命名空间

DLL

描述

System.Data

.Odbc

System.Data.dll

这个命名空间可以连接到任何提供了支持 ODBC (Open Database Connectivity,开放的数据连接)标准的数据库。大多数数据库都支持这个标准,但是通常应该避免使用,而应该使用更专门的驱动,因为它们会更有效。

System.Data

.OleDb

System.Data.dll

OleDb 是基于 COM 标准的数据库的驱动,另外,大量关系型数据库都提供了支持该标准的驱动,但是,如果有可能,要使用更专门的驱动。这个命名空间通常用于连接到 Access 数据库或 Excel 电子表格,它们没有 .NET 版本的驱动。

System.Data

.SqlClient

System.Data.dll

这是 Microsoft SQL Server 本地 .NET版本的驱动,它支持所有 SQL Server 的版本,是使用 SQL Server 的不二选择。书中示例都使用这个命名空间。

System.Data

.OracleClient

System.Data

.OracleClient.dll

这是 Oracle 数据库的本地 .NET 版本的提供程序,由 Microsoft 提供,随 .NET 框架一起发布。

Oracle.DataAccess

.Client

Oracle.DataAccess

.Client.dll

.NET 版本的 Oracle 数据提供程序(ODP.NET)是由 Oracle 开发的 .NET版本的数据库提供程序,在www.oracle.com/technology/software/tech/windows/odpnet。

IBM.Data.DB2

IBM.Data.DB2.dll

这是由 IBM 开发的本地 .NET 版本的提供程序,随数据库发布。

MySql.Data

.MySqlClient

MySql.Data.dll

这是由 MySQL 组开发的开源的本地 .NET 版本的提供程序。可以从

dev.mysql.com/downloads/connector/net 下载。

FirebirdSql.Data

.FirebirdClient

FirebirdSql.Data

.FirebirdClient.dll

这是开源数据库 Firebird 的本地提供程序,可以从 www.firebirdsql.org/index.php?op=files&id=netprovider 下载。

 

为演示如何使用其他的 .NET 版本的提供程序,连接到关系型数据库管理系统,现在给出一个例子,连接到 Firebird 的employee 示例数据库。运行这个示例,需要安装 Firebird 数据库引擎和 Firebird 的 .NET 版本的提供程序组件,可从 http://www.firebirdsql.org 下载;还需要在本地运行 Firebird 数据库服务。

 

open System.Configuration

open System.Collections.Generic

open System.Data

open FirebirdSql.Data.FirebirdClient

open System.Data.Common

open System

 

// firebird connection string

let connectionString =

  @"Database=C:\ProgramFiles\Firebird\" +

  @"Firebird_2_0\examples\empbuild\EMPLOYEE.FDB;"+

  @"User=SYSDBA;"+ "Password=masterkey;" +

  @"Dialect=3;"+ "Server=localhost";

 

// open firebird connection

let openFBConnection() =

  letconnection = new FbConnection (connectionString)

  connection.Open();

  connection

 

// create a reader to read all theinformation

let openConnectionReader cmdString =

  letconn = openFBConnection()

  letcmd = conn.CreateCommand(CommandText = cmdString,

                             CommandType =CommandType.Text)

  letreader = cmd.ExecuteReader(CommandBehavior.CloseConnection)

  reader

 

// read a row from the database and convertinto a dictionary

let readOneRow (reader: #DbDataReader) =

  ifreader.Read() then

    letdict = new Dictionary<string, obj>()

    forx = 0 to (reader.FieldCount - 1) do

    dict.Add(reader.GetName(x),reader.Item(x))

    Some(dict)

  else

    None

 

// execute a database command creating asequence of the results

let execCommand cmdString =

  Seq.generate

    //This function gets called to open a conn and create a reader

    (fun() -> openConnectionReader cmdString)

    //This function gets called to read a single item in

    //the enumerable for a reader/conn pair

     (funreader -> readOneRow(reader))

     (fun reader -> reader.Dispose())

 

// select all from the Employee's table

let employeeTable =

  execCommand

    "select* from Employee"

 

// print out the Employee's information

for row in employeeTable do

  forcol in row.Keys do

    printfn"%s = %O " col (row.Item(col))

 

前面示例的运行结果如下:

 

...

EMP_NO = 145

FIRST_NAME = Mark

LAST_NAME = Guckenheimer

PHONE_EXT = 221

HIRE_DATE = 02/05/1994 00:00:00

DEPT_NO = 622

JOB_CODE = Eng

JOB_GRADE = 5

JOB_COUNTRY = USA

SALARY = 32000

FULL_NAME = Guckenheimer, Mark

 

注意

只有非常小的变化,把 SQL Server AdventureWorks 的 contact 表转换成对Firebird employee 数据库表employee 的查询。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
原文地址:https://github.com/andolove/Data 感谢作者!很实用 简单的Ado.net数据访问客户端。 数据库访问入口 获取IDbClient 在开始之前,先添加一个数据库访问入口。当然,也可以使用任何你喜欢的方式来创建IDbClient(的实现类)实例。 public static class Db { private static readonly Dictionary<string, IDbClient> KnownClients = new Dictionary<string, IDbClient>(); public static IDbClient Northwind { get { return GetClient("Northwind", "server=.;database=Northwind;trusted_connection=true;"); } } private static IDbClient GetClient(string name, string connectionString) { IDbClient client; if (KnownClients.TryGetValue(name, out client)) return client; lock (KnownClients) { if (KnownClients.TryGetValue(name, out client)) return client; // 创建IDbClient的实例 client = new SqlDbClient(connectionString); KnownClients.Add(name, client); } return client; } } 现在,可以使用Db.Northwind来访问SQLServer的Northwind示例数据库了。 访问其他数据库 如果要访问MySql,可以用几行代码实现一个面向MySql的IDbClient实现。下面以使用 MySql.Data.dll 作为MySql .net客户端提供器为例。 /// <summary> /// Mysql数据库访问客户端。 /// </summary> public class MysqlDbClient : AbstractDbClient { private readonly string _connectionString; /// <summary> /// 使用指定的数据库类型和连接字符串初始化<see cref="SqlDbClient"/>的新实例。 /// </summary> /// <param name="connectionString">连接字符串。</param> public MysqlDbClient(string connectionString) { ArgAssert.NotNullOrEmptyOrWhitespace(connectionString, "connectionString"); _connectionString = connectionString; } /// <summary> /// 获取当前实例所使用的数据库连接字符串。 /// </summary> public override string ConnectionString { get { return _connectionString; } } /// <summary> /// 获取当前实例所使用的<see cref="DbProviderFactory"/>实例。 /// </summary> protected override DbProviderFactory Factory { get { return MySql.Data.MySqlClient.MySqlClientFactory.Instance; } } } 现在可以创建MySql的访问客户端了: IDbClient client = new MysqlDbClient("server=.;database=MySqlDb;uid=user;pwd=password"); 类似的,可以创建访问Oracle,Sqlite或是其他数据库的客户端,只需要找到对应的DbProviderFactory实例即可。 基本数据库操作 基础CRUD // 查询 string productName = (string)Db.Northwind.Scalar( "SELECT ProductName FROM Products WHERE ProductID=115"); DataTable productTable = Db.Northwind.DataTable("SELECT * FROM Products"); // 更新 int affectedRows = Db.Northwind.Execute( "UPDATE Products SET ProductName='The Name' WHERE ProductID=115"); // 在没有命中一行的时候抛出异常 int expectedSize = 1; Db.Northwind.SizedExecute( expectedSize, "UPDATE Products SET ProductName='The Name' WHERE ProductID=115"); // 获取一行 IDataRecord record = Db.Northwind.GetRow( "SELECT ProductName, SupplierID FROM Products WHERE ProductID=115"); int supplierId = Convert.ToInt32(record["SupplierID"]); // 在不用在意资源释放的情况下使用DataReader,利用了foreach的机制,在循环结束后DataReader会自动关闭 IEnumerable<IDataRecord> rows = Db.Northwind.Rows( "SELECT ProductName, SupplierID FROM Products WHERE ProductID=115"); foreach (IDataRecord row in rows) { Console.WriteLine(row["ProductName"]); } 使用参数和调用存储过程 // 使用参数 DbParameter parameter = Db.Northwind.CreateParameter(); parameter.DbType = DbType.String; parameter.ParameterName = "CustomerID"; parameter.Value = "ALFKI"; parameter.Direction = ParameterDirection.Input; // 调用存储过程 CustOrderHist @CustomerID DataSet ds = Db.Northwind.DataSet( "CustOrderHist", new[] { parameter }, CommandType.StoredProcedure); // 使用DbClientParamEx中的扩展方法快速创建参数(需要using Data命名空间) DbParameter[] parameters = new[] { Db.Northwind.CreateParameter("id", DbType.Int32, 115, direction: ParameterDirection.Input), Db.Northwind.CreateParameter("name", DbType.String, "Ikura", 5) }; Db.Northwind.DataSet("SELECT * FROM Products WHERE ProductName=@name OR ProductID=@id", parameters); 使用Mapper IMapper<T>接口定义了从IDataRecord到T类型的映射,可以用过实现该接口,以便从数据库读取并创建特定类型实例及实例的集合。 public class Product { public int ProductID; public string ProductName; } public class ProductMapper : IMapper<Product> { public Product MapRow(IDataRecord record, int rowNum) { var product = new Product(); product.ProductID = Convert.ToInt32(record["ProductID"]); product.ProductName = record["ProductName"].ToString(); return product; } } 利用上面的ProductMapper,我们可以直接从查询中创建Product实例了。 // 获取一个实例 Product product = Db.Northwind.Get( new ProductMapper(), "SELECT * FROM Products WHERE ProductID=115"); // 获取实例的集合 IList<Product> products = Db.Northwind.List(new ProductMapper(), "SELECT * FROM Products"); Mappers类中已经定义了部分简单类型的Mapper实现,以便实现便捷的查询。 // 使用已定义好的简单Mapper IList<string> productNames = Db.Northwind.List( Mappers.String(), "SELECT ProductName FROM Products"); IList<int> productIds = Db.Northwind.List( Mappers.Int32(), "SELECT ProductID FROM Products"); // 使用实现IConvertible的类型创建Mapper IList<DateTime> orderDates = Db.Northwind.List( Mappers.Convertible<DateTime>(), "SELECT OrderDate FROM Orders"); 使用事务 使用CreateTransaction方法来获取一个ITransactionKeeper事务容器。获取到的事务容器自身也实现了IDbClient,可以在其上进行各种CRUD操作。 事务的最后,别忘了Commit。 ITransactionKeeper同时也实现了IDisposable接口,其Dispose方法能够在事务没有提交时进行事务回滚(如果已经提交,则什么也不做),利用这个机制和C#的using语法,可以很方便的编写一个在出现异常时回滚的事务操作。 using (ITransactionKeeper tran = Db.Northwind.CreateTransaction()) { tran.Execute("UPDATE Products SET ProductName='The Name' WHERE ProductID=115"); tran.Execute("UPDATE Products SET ProductName='The Name2' WHERE ProductID=118"); tran.Commit(); } Dynamic扩展 在Data.Dynamic命名空间的ObjectiveExtension类中,定义了一套IDbClient的扩展方法,能够使用更快捷的方式进行数据库操作。 .net对象传参 这些扩展方法具有与IDbClient中的方法很类似的签名,但能够接收一个用于存放参数信息的.net对象,以节省许多编码量(是的,和Dapper、ServiceStack.OrmLite很相似)。 通过这些扩展方法,上面使用参数的示例可以这样写了: DataSet ds = Db.Northwind.DataSet( "CustOrderHist", new { CustomerID = "ALFKI" }, CommandType.StoredProcedure); DataTable dt = Db.Northwind.DataTable( "SELECT * FROM Products WHERE ProductName=@name OR ProductID=@id", new { name = "Ikura", id = 115 }); 获取类型实例 现在不指定Mapper就可以直接进行对象查询了。 Product product = Db.Northwind.Get<Product>("SELECT * FROM Products WHERE ProductID=115"); IList<Product> products = Db.Northwind.List<Product>("SELECT * FROM Products"); IList<DateTime> orderDates = Db.Northwind.List<DateTime>("SELECT OrderDate FROM Orders"); 在这些方法内部,会在运行时动态生成对应的Mapper,并且生成一次以后,信息会被缓存下来,不需要每次都重新创建。当然,因为做了更多的是事情,它还是会比非扩展的原生版本慢那么一点点。 也可以使用匿名对象作为实体模板,在许多场景尤其是处理包含少量字段(但又多于1个)时尤其方便。 var template = new { ProductID = 0, ProductName = string.Empty }; var productsByTemplate = Db.Northwind.TemplateList(template, "SELECT * FROM Products"); 关于字段名称的匹配 .net对象的属性和公共字段使用Pascal命名法,但数据库规范中的字段命名法可能不一样,比如MySql的snake_case命名法;而且也有太多的数据库设计使用“意识流”了。为了解决这个命名差异问题,查询结果映射到非匿名对象字段时支持字段名称的模糊匹配,具体规则如下,越靠前的规则优先级越高: 查询结果的字段名称和对象字段名称完全一致; 大小写不敏感的匹配;例:查询结果字段goodName可映射到对象字段GoodName。 查询结果的字段名称移除下划线(头尾的下划线将保留)之后,再进行大小写不敏感的匹配;例:查询结果字段good_name可映射到对象字段GoodName;_goodName不会映射到GoodName,因为头尾的下划线不会被忽略。 字体匹配时,考前的规则将优先进行匹配,没有匹配到的字段再使用下一优先级的规则进行匹配。若所有规则都为命中,则对象字段将在映射中被忽略从而保持字段类型的默认值。 注意:使用匿名对象作为模板查询时,匿名对象的字段名称需和查询结果的字段名称完全匹配,不支持模糊匹配。 Indexing扩展 在Data.Indexing命名空间的IndexingExtension类中,定义了另外一套IDbClient的扩展方法,能够基于索引访问传入的参数。 记得string.Format方法吗: string.Format("My name is {0}, I'm {1} years old.", "John Doe", 8); 类似的,这些扩展方法用起来是这个样子的: DataTable dt = Db.Northwind.DataTable( "SELECT * FROM Products WHERE ProductName=@0 OR ProductID=@1", "Ikura", 115); IList<Product> products = Db.Northwind.List<Product>( "SELECT * FROM Products WHERE ProductID IN (@0, @1)", 15, 16); 通常在一个地方并不混用两套扩展。Dynamic扩展会更泛用一些,但在一些特定的场景下,使用Indexing扩展也是个好主意。还有,这套扩展方法速度会更快一些。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值