7、實(shí)現(xiàn)文章發(fā)布、編輯、閱覽和刪除功能
1.前言
因?yàn)楸鞠盗惺?NET Core 系列,本文中所有敘述的是基于.NET Core 1.1版本的數(shù)據(jù)訪問(wèn)層接口。為什么需要強(qiáng)調(diào)是.Net Core 1.1呢?由于在2017年Q3發(fā)布的.NET Core 2.0中通過(guò)官網(wǎng)的Apis文檔可以看出,在.NET Core 2.0時(shí)代已經(jīng)將.NET Framework的ADO.NET整套體系完整覆蓋。同時(shí)也讓我十分糾結(jié),到底要不要寫這篇文章。從而造成我對(duì)整個(gè)系列的編寫產(chǎn)生了動(dòng)搖?;仡^想想,還是寫下來(lái)吧!就當(dāng)是自己的一次學(xué)習(xí)筆記的記錄。
2. ADO.NET的前世今生
ADO.NET的名稱起源于ADO(ActiveX Data Objects),是一個(gè)COM組件庫(kù),用于在以往的Microsoft技術(shù)中訪問(wèn)數(shù)據(jù)。之所以使用ADO.NET名稱,是因?yàn)镸icrosoft希望表明,這是在NET編程環(huán)境中優(yōu)先使用的數(shù)據(jù)訪問(wèn)接口。
ADO.NET可讓開發(fā)人員以一致的方式存取資料來(lái)源(例如 SQL Server 與 XML),以及透過(guò) OLE DB 和 ODBC 所公開的資料來(lái)源。資料共用的消費(fèi)者應(yīng)用程序可使用ADO.NET 來(lái)連接至這些資料來(lái)源,并且擷取、處理及更新其中所含的資料。
ADO.NETt可將資料管理的資料存取分成不連續(xù)的元件,這些元件可分開使用,也可串聯(lián)使用ADO.NET也包含 .NET Framework 資料提供者,以用于連接資料庫(kù)、執(zhí)行命令和擷取結(jié)果。這些結(jié)果會(huì)直接處理、放入ADO.NET DataSet 物件中以便利用機(jī)器操作 (Ad Hoc)的方式公開給使用者、與多個(gè)來(lái)源的資料結(jié)合,或在各層之間進(jìn)行傳遞。DataSet 物件也可以與.NET Framework 資料提供者分開使用,以便管理應(yīng)用程序本機(jī)的資料或來(lái)自 XML 的資料。
ADO.NET類別 (Class) 位于 System.Data.dll 中,而且會(huì)與 System.Xml.dll 中的XML 類別整合。
ADO.NET可為撰寫 Managed 程式碼的開發(fā)人員提供類似于ActiveX Data Objects (ADO)提供給原生元件物件模型 (Component Object Model,COM)開發(fā)人員的功能。建議使用ADO.NET而非ADO來(lái)存取.NET 應(yīng)用程序中的資料。
ADO .NET會(huì)提供最直接的方法,讓開發(fā)人員在 .NET Framework 中進(jìn)行資料存取。
------- 摘自百度百科
我什么要摘出這一段呢?因?yàn)樵?NET Core 1.1里根本不存在DataSet這個(gè)類只實(shí)現(xiàn)了DataTable,也不存在DataAdapter。在2002-2009年這7年間,大量的應(yīng)用均是使用這類適配存取的方式去編寫應(yīng)用。簡(jiǎn)單、高效、門檻低。2009年后各種ORM、MVC、MVP、MVVM、IoC等各種架構(gòu)和技術(shù)開始流行。DataAdapter+DataSet+ASP.NET WEBFORM的套路開始走下神壇。
而.NET Core 1.1可以說(shuō)是微軟一個(gè)重要的云計(jì)算計(jì)劃的其中一個(gè)棋子。并且微軟本質(zhì)的目標(biāo)也并非鼓勵(lì)將已有應(yīng)用向.NET Core遷移。所以在.NET Core 1.1版本只實(shí)現(xiàn)了一個(gè)基礎(chǔ)閉環(huán)。.NET Standard + .NET Core MVC + EntityFramework Core。畢竟步子邁得太大容易扯著蛋。
3、用基礎(chǔ)的劍法打出一套增、刪、改、查
讓我們看看.NET Core 為我們提供了什么基礎(chǔ)劍法。
增、刪、改、查均是SQL語(yǔ)句的命令,所以只要存在能向數(shù)據(jù)庫(kù)發(fā)送SQL腳本的接口則可以實(shí)現(xiàn),Command,要發(fā)送腳本總要知道腳本往哪里發(fā)找到了Connection,執(zhí)行完腳本數(shù)據(jù)庫(kù)向我們回發(fā)結(jié)果總要有一個(gè)承載 Reader、 Record。如果執(zhí)行的是多條腳本,并且需要保證腳本一次成功,我們找到了Transaction
3.1 IDbConnection
和數(shù)據(jù)庫(kù)交互,必須連接它,連接幫助指明數(shù)據(jù)庫(kù)服務(wù)器,數(shù)據(jù)庫(kù)名字,用戶名,密碼和其他需要的參數(shù)。Connection會(huì)被Command對(duì)象使用,這樣就能夠知道是在哪個(gè)數(shù)據(jù)源上面執(zhí)行命令。其實(shí)我就是我們?cè)L問(wèn)數(shù)據(jù)庫(kù)時(shí)最初的那個(gè)連接數(shù)據(jù)庫(kù)字符串。
現(xiàn)在很多學(xué)習(xí)編程的新同學(xué),都是依賴百度/谷歌去直接搜索解決方法。但是學(xué)習(xí)的本質(zhì)都是從官方例子開始理解的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | using System; using System.Data; namespace IDbConnectionSample { class Program { static void Main( string [] args) { IDbConnection connection; // First use a SqlClient connection connection = new System.Data.SqlClient.SqlConnection( @"Server=(localdb)\V11.0" ); Console.WriteLine( "SqlClient\r\n{0}" , GetServerVersion(connection)); connection = new System.Data.SqlClient.SqlConnection( @"Server=(local);Integrated Security=true" ); Console.WriteLine( "SqlClient\r\n{0}" , GetServerVersion(connection)); // Call the same method using ODBC // NOTE: LocalDB requires the SQL Server 2012 Native Client ODBC driver connection = new System.Data.Odbc.OdbcConnection( @"Driver={SQL Server Native Client 11.0};Server=(localdb)\v11.0" ); Console.WriteLine( "ODBC\r\n{0}" , GetServerVersion(connection)); connection = new System.Data.Odbc.OdbcConnection( @"Driver={SQL Server Native Client 11.0};Server=(local);Trusted_Connection=yes" ); Console.WriteLine( "ODBC\r\n{0}" , GetServerVersion(connection)); // Call the same method using OLE DB connection = new System.Data.OleDb.OleDbConnection( @"Provider=SQLNCLI11;Server=(localdb)\v11.0;Trusted_Connection=yes;" ); Console.WriteLine( "OLE DB\r\n{0}" , GetServerVersion(connection)); connection = new System.Data.OleDb.OleDbConnection( @"Provider=SQLNCLI11;Server=(local);Trusted_Connection=yes;" ); Console.WriteLine( "OLE DB\r\n{0}" , GetServerVersion(connection)); } public static string GetServerVersion(IDbConnection connection) { // Ensure that the connection is opened (otherwise executing the command will fail) ConnectionState originalState = connection.State; if (originalState != ConnectionState.Open) connection.Open(); try { // Create a command to get the server version // NOTE: The query's syntax is SQL Server specific IDbCommand command = connection.CreateCommand(); command.CommandText = "SELECT @@version" ; return ( string )command.ExecuteScalar(); } finally { // Close the connection if that's how we got it if (originalState == ConnectionState.Closed) connection.Close(); } } } } |
這個(gè)例子主要是演示了三種鏈接驅(qū)動(dòng)的使用方式分別是:SqlClient、ODBC、OLE DB 但是很遺憾NET Core 1.1只提供了SqlClient,但是在官方的Apis2.0文檔中是可以找ODBC、OLE DB、OracleClient。除了官方的驅(qū)動(dòng)開源的驅(qū)動(dòng)提供NET Core 1.1的還有Npgsql(postgresql數(shù)據(jù)庫(kù)),Mysql的官方驅(qū)動(dòng)尚未支持.NET Core,在nuget上可以找到Mysql.Data(8.0.8)預(yù)覽版的驅(qū)動(dòng)可以支持.NET Core 1.1
下面我們以Npgsql來(lái)演示一次上述例子。
using System;using System.Data;using Npgsql;namespace IDbConnectionSample { class Program { static void Main(string[] args) { IDbConnection connection; connection = new NpgsqlConnection("Server=192.168.1.41;Port=5432;User Id=postgres;Password=postgres;"); Console.WriteLine("Npgsql:\r\n{0}", GetServerVersion(connection)); Console.ReadKey(); } public static string GetServerVersion(IDbConnection connection) { ConnectionState originalState = connection.State; if (originalState != ConnectionState.Open) connection.Open(); try { IDbCommand command = connection.CreateCommand(); command.CommandText = "SELECT version()";//pgsql獲取版本的函數(shù)是Version() return (string)command.ExecuteScalar(); } finally { if (originalState == ConnectionState.Closed) connection.Close(); } } } }
3.2 IDbCommand
成功與數(shù)據(jù)建立連接后,就可以用Command對(duì)象來(lái)執(zhí)行查詢、修改、插入、刪除等命令;Command對(duì)象常用的方法有ExecuteReader()方法、ExecuteScalar()方法和ExecuteNonQuery()方法;插入數(shù)據(jù)可用ExecuteNonQuery()方法來(lái)執(zhí)行插入命令。
看例子:
using System;using System.Data;using Npgsql;using System.Collections.Generic;namespace IDbConnectionSample { class Program { static void Main(string[] args) { IDbConnection connection; connection = new NpgsqlConnection("Server=192.168.1.41;Port=5432;User Id=postgres;Password=postgres;Database=test"); Console.WriteLine("Npgsql:\r\n{0}", GetServerVersion(connection)); Console.WriteLine("InsertUser:\r\n{0}", InsertUser(connection, "InsertUser" + DateTime.Now.Ticks)); foreach (var item in AllUserName(connection)) { Console.WriteLine("AllUserName:\r\n{0}", item); } Console.ReadKey(); } /// <summary> /// 查單值 /// </summary> /// <param name="connection"></param> /// <returns></returns> public static string GetServerVersion(IDbConnection connection) { ConnectionState originalState = connection.State; if (originalState != ConnectionState.Open) connection.Open(); try { IDbCommand command = connection.CreateCommand(); command.CommandText = "SELECT version()";//pgsql獲取版本的函數(shù)是Version() return (string)command.ExecuteScalar(); } finally { if (originalState == ConnectionState.Closed) connection.Close(); } } /// <summary> /// 增加一個(gè)用戶 /// </summary> /// <param name="connection"></param> /// <returns></returns> public static bool InsertUser(IDbConnection connection, string userName) { ConnectionState originalState = connection.State; if (originalState != ConnectionState.Open) connection.Open(); try { IDbCommand command = connection.CreateCommand(); //實(shí)際項(xiàng)目不要使用這種方式。僅演示使用,為什么?自己探索。 //自定義的user表需要加上架構(gòu)限定名 command.CommandText = "INSERT INTO public.user(name) VALUES ('" + userName + "')";//pgsql獲取版本的函數(shù)是Version() return command.ExecuteNonQuery() > 0; } finally { if (originalState == ConnectionState.Closed) connection.Close(); } } /// <summary> /// 查找所有用戶的名稱 /// </summary> /// <param name="connection"></param> /// <returns></returns> public static IList<string> AllUserName(IDbConnection connection) { IList<string> allUserName = new List<string>(); ConnectionState originalState = connection.State; if (originalState != ConnectionState.Open) connection.Open(); try { IDbCommand command = connection.CreateCommand(); //自定義的user表需要加上架構(gòu)限定名 command.CommandText = "select name from public.user";//pgsql獲取版本的函數(shù)是Version() var reader = command.ExecuteReader(); while (reader.Read()) { //根據(jù)select的語(yǔ)句中第0位是name allUserName.Add(reader.GetString(0)); } } finally { if (originalState == ConnectionState.Closed) connection.Close(); } return allUserName; } } }
執(zhí)行結(jié)果:
3.3 IDataReader
許多數(shù)據(jù)操作要求開發(fā)人員只是讀取一串?dāng)?shù)據(jù)。DataReader對(duì)象允許開發(fā)人員獲得從Command對(duì)象的SELECT語(yǔ)句得到的結(jié)果??紤]性能的因素,從DataReader返回的數(shù)據(jù)都是快速的且只是“向前”的數(shù)據(jù)流。這意味著開發(fā)人員只能按照一定的順序從數(shù)據(jù)流中取出數(shù)據(jù)。這對(duì)于速度來(lái)說(shuō)是有好處的,但是如果開發(fā)人員需要操作數(shù)據(jù),并且這些數(shù)據(jù)含有復(fù)雜邏輯,最好還是先將數(shù)據(jù)轉(zhuǎn)化為DataTable(1.1沒(méi)有DataSet)、結(jié)構(gòu)體或簡(jiǎn)單失血模型。因?yàn)镈ataReader和數(shù)據(jù)庫(kù)間產(chǎn)生著持續(xù)的鏈接,知道度完后才會(huì)關(guān)閉連接。如果在read的過(guò)程中進(jìn)行復(fù)雜的邏輯操作,那么對(duì)于并發(fā)來(lái)說(shuō)那將是一個(gè)災(zāi)難。
從上圖中沒(méi)有找到任何讀取數(shù)據(jù)列的操作,主要是因?yàn)镮DataReader是繼承IDataRecord,主要的讀取操作均實(shí)現(xiàn)于IDataRecord
http://www.cnblogs.com/maydear/p/5925180.html