C# Notizen 15 数据库

很多应用程序都需要与数据库交互。数据库是一个数据仓库,与文本文件和 XML 文件极其相似。可以想见,使用文件来存储数据并非总是最好的方法,因此大多数应用程序都使用关系数据库。关系数据库将数据组织成表、行和记录。每个表都由行组成,而每行表示一条记录。行被进一步划分为列。在同一个表中,每行的列结构都相同。
.NET Framework提供了大量通过ADO.NET与数据库进行交互的类,而ADO.NET是一个对象库,让编写使用数据库和LINQ to ADO.NET的应用程序更简单。

一、ADO.NET
ADO.NET 库是一个功能丰富的框架,使用它可创建能够检索和更新关系数据库中信息的应用程序。为遵循语言和平台独立的理念,ADO.NET是基于数据提供程序的概念设计的。ADO.NET支持的每种数据库系统(如 SQL Server、Oracle和DB2)都有一个提供程序,它实现了连接到数据库、执行查询和更新数据的机制。每个提供程序实现的接口都相同,这让您能够编写独立于底层数据库的代码。
ADO.NET中的一个主要类是DataSet,它表示数据库的一部分。DataSet不要求始终连接到数据库,这让您能够以离线方式工作。等到需要更新数据库和 DataSet 时,您才需要将DataSet连接到数据库,并执行所需的更新。

ps:ADO.NET数据提供程序
ADO.NET数据提供程序封装了连接到数据库、执行命令以及检索结果所需的逻辑。它在底层数据源和应用程序代码之间提供了一个轻量级层。
当前,.NET Framework自带了 5个数据提供程序,其中每个都位于一个独立的命名空间中:
用于SQL Server的数据提供程序(System.Data.SqlClient)
用于OLE DB的数据提供程序(System.Data.OleDb)
用于ODBC的数据提供程序(System.Data.Odbc)
用于Oracle的数据提供程序(System.Data.OracleClient)
EntityClient提供程序(System.Data.EntityClient)

虽然SQL Server和Oracle都支持OLE DB,但是应使用专用于SQL Server或Oracle的提供程序,因为它们针对这些数据库系统进行了优化。
为此,DataSet必须表示数据库表以及这些表之间的关系。由数据库表组成的集合可通过属性 Tables 访问,其中每个表都用一个 DataTable 实例表示。属性 Relations 是一个由DataRelation实例组成的集合,其中每个DataRelation都表示两个表之间的一种关系。
查询数据库时常创建 DataTable,它表示数据库中的一个表。数据库表的每列都用一个DataColumn表示,这是通过DataTable的Columns属性暴露的。DataTable还包含一个Rows属性,它包含一系列DataRow对象,其中每个DataRow对象都对应于数据库表中的一行。
如果将DataSet视为数据库的抽象,便需要使用某种方式在DataSet和底层数据库之间搭建桥梁。这是使用DataAdapter完成的,它用于在数据源和DataSet之间交换数据,这种交换主要是通过方法Fill和Update完成的。这样做带来的好处是,一个DataSet可表示多个数据库的表。

ps:使用DataReader进行只读访问
DataSet和DataAdapter都支持与数据库的双向交互,能够读取和写入数据。如果只需要读取数据(这种情况很常见),那么可以创建DbDataReader或其派生类(如SqlDataReader)的实例。要创建DataReader,最简单的方式是使用DbCommand的方法ExecuteReader。
DbDataReader 能够以只读、只进的方式连续访问一个表集合,这常被称为灭火水龙带游标(fire hose cursor),因为这类似于访问快速、只进、连续不断的数据流中的数据,而这种数据流像灭火水龙带流出的水流:流量大、只进、连续不断。
因此,它们属于轻量级对象,最适合用于给控件填充数据,然后断开数据库连接。
最后,需要使用DbConnection表示到数据源的连接,并使用DbCommand表示数据库命令,如SQL语句或存储过程。创建DbConnection后,便可在众多DbCommand之间共享它。
每个数据提供程序都提供了DbConnection和DbCommand的子类,并专门针对相应的数据库系统对这些子类进行了定制。例如,用于 SQL Server 的数据提供程序提供了SqlConnection和SqlCommand类。

ps:必不可少的引用
要使用 ADO.NET,需要确保项目包含到程序集 System.Data 的引用,并包含要使用的数据提供程序对应的命名空间。

如下示例演示了如何执行查询,其中使用了ADO.NET和用于SQL Server的数据提供程序提供的类:

using (SqlConnection connection = new SqlConnection())
{
    connection.ConnectionString = @"Integrated Security=SSPI;database=AdventureWorksLT2008;server=(local)\SQLEXPRESS";

    try
    {
        using (SqlCommand command = new SqlCommand())
        {
            command.Connection = connection;
            command.CommandText = @"SELECT *
                FROM [AdventureWorksLT2008].[SalesLT].[Customer]
                WHERE [CompanyName] = 'A ZZ Home'";
            connection.Open();
            SqlDataReader reader = command.ExecuteReader();
            while (reader.Read())
            {
                Console.WriteLine("{0} {1} {2}",
                      reader.GetString(2),
                     reader.GetString(3),
                      reader.GetString(5));
            }
            connection.Close();
        }
    }
    catch (SqlException e)
    {
        Console.WriteLine("An error occurred: {0}", e.Message);
    }
}

为确保数据库命令和数据库连接占用的资源得以释放,且连接得以关闭,最好将它们放在一条using语句中。

ps:连接池
大多数数据提供程序都支持连接池。可将连接池视为一组可用的数据库连接,应用程序需要连接时,提供程序将从连接池中提取下一个可用的连接。应用程序关闭连接时,连接将归还给连接池,可供下一个需要连接的应用程序使用。
二、LINQ to ADO.NET
LINQ to ADO.NET实际上是 3项独立的技术,让您能够与关系数据库交互。下图说明了这些LINQ to ADO.NET技术之间的关系,以及这些技术与其他支持LINQ的数据源和高级编程语言(如C#)之间的关系。

Paste_Image.png

2.1 LINQ to DataSet
LINQ to DataSet建立在现有ADO.NET架构的基础之上,能够轻松、快捷地创建查询,以查询存储在 DataSet 中的数据。它并非要取代 ADO.NET 在应用程序中的位置,而是旨在让您能够使用LINQ语法编写查询,并填补DataSet有限的查询功能留下的空白。

要使用LINQ to DataSet,必须首先填充DataSet。加载数据后,便可使用LINQ to Objects和LINQ to XML的方法进行查询。这些查询可针对DataSet中的一个表,也可针对多个表(通过使用查询运算符Join和GroupJoin)。
从功能上说,如下程序与上一个示例等价,但是使用的是LINQ to DataSet查询。该程序清单首先创建了一个 SqlDataAdapter,然后使用数据填充 DataSet 实例。这些代码对ADO.NET和 LINQ to DataSet来说都是必不可少的,但是通过对 DataTable执行扩展方法AsEnumerable,可对数据执行标准LINQ查询。

string connectionString = @"Integrated Security=SSPI;database=AdventureWorksLT2008;server=(local)\SQLEXPRESS";

string selectSQL = @"SELECT *FROM [AdventureWorksLT2008].[SalesLT].[Customer]";

SqlDataAdapter adapter = new SqlDataAdapter(selectSQL, connectionString);
DataSet ds = new DataSet();
adapter.Fill(ds);
foreach (var customer in ds.Tables[0].AsEnumerable().Where(row => row.Field<string>("CompanyName") == "A ZZ Home"))
{
    Console.WriteLine("{0} {1} {2}",
      customer.Field<string>("Title"),    
      customer.Field<string>("FirstName"),
      customer.Field<string>("LastName");
}

就像LINQ to XML添加了专门用于XML的扩展一样,LINQ to DataSet也添加了专门针对DataSet的扩展。这些扩展使得查询DataRow对象集合更容易,能够比较DataRow对象以及直接访问DataRow的列值。

ps:必不可少的引用
要使用LINQ to DataSet,需确保项目包含指向如下程序集的引用。
(1)System.Core。
(2)System.Data。
(3)System.Data.DataSetExtensions。
(4)System.Data.Common 或 System.Data.SqlClient,这取决于将如何连接到数据库。
另外,还需包含命名空间System.Linq和System.Data。

2.2 LINQ to SQL
LINQ to SQL让您能够直接编写针对SQL数据库的查询,且使用的语法与查询内存中的集合和其他LINQ数据源时相同。ADO.NET将数据库映射到由DataSet表示的概念数据模型,而LINQ to SQL将数据库映射到应用程序代码中的对象模型。应用程序执行时,LINQ to SQL将把LINQ查询转换为SQL查询,随后这种查询被发送给数据库执行。结果返回后,LINQ to SQL将它们转换为数据模型对象。
要使用LINQ to SQL,必须首先创建表示数据库的对象模型。为此,有以下两种方式。

  • 使用对象关系设计器(O/R设计器):它是Visual Studio 2010的一部分,提供了丰富的用户界面,用于从现有数据库创建对象模型。O/R设计器只支持SQL Server学习版数据库以及SQL Server 2000、2005和 2008数据库。
  • 使用命令行工具 SQLMetal:仅当使用的是大型数据库或 O/R 设计器不支持的数据库时,才应使用它。
    决定如何生成对象模型后,需要决定要生成哪种类型的代码。使用O/R设计器时,可生成C#源代码来提供基于属性的映射。如果使用的是SQLMetal命令行工具,就可生成包含映射元数据的外部XML文件。

ps:创建对象模型
实际上还有第三种创建对象模型的方式,即在代码编辑器中手动编写对象模型。不推荐这样做,因为容易出错,尤其是在创建表示现有数据库的对象模型时。
可使用代码编辑器来修改或精制O/R设计器或命令行工具SQLMetal生成的代码。
创建对象模型后,便可通过编写LINQ查询在应用程序中使用它。如下示例包括定义 DataContext、执行查询和显示结果:

DataContext dataContext = new DataContext(@"Integrated Security=SSPI;database=AdventureWorksLT2008;server=(local)\SQLEXPRESS");

Table<Customer> Customers = dataContext.GetTable<Customer>();
IQueryable<Customer> query =
    from customer in customers
    where customer.CompanyName == "A ZZ Home"
    select customer;

foreach(Customer customer in query)
{
    Console.WriteLine("{0} {1} {2}",
      customer.Title,
     customer.FirstName,
      customer.LastName);
}

ps:必须包含的引用
要使用LINQ to SQL,务必在项目中包含指向如下程序集的引用。
(1)System.Core。
(2)System.Data.Linq。
另外,还需包含命名空间System.Linq和System.Data.Linq。
首先,必须通过 DataContext 在对象模型和数据库之间建立连接。然后,创建了一个Table<T>,它充当要查询的表。可认为DataContext类似于DBConnection,而Table<T>类似于DataTable,虽然实际上并非如此。最后,定义并执行查询。

只需编写一个LINQ查询并执行它,就可选择数据(也叫投影)。LINQ to SQL还能够添加、修改和删除数据库中的数据。当使用LINQ to SQL执行修改操作时,修改的都只是本地缓存。只有对DataContext实例调用方法SubmitChange后,所做的修改才会发送到数据库。
要在数据库中添加记录,只需创建合适数据模型对象的一个新实例,再调用Table<T>的方法 InsertOnSubmit。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容