1. 程式人生 > >ORM框架之------Dapper,Net下無敵的ORM

ORM框架之------Dapper,Net下無敵的ORM

官網 結果集 vid 方便 喜歡 完全 字符 sqlite數據庫 工作

一,介紹:Dapper是一款輕量級ORM工具。如果你在小的項目中,使用Entity Framework、NHibernate 來處理大數據訪問及關系映射,未免有點殺雞用牛刀。你又覺得ORM省時省力,這時Dapper 將是你不二的選擇。

  ---ORM框架的核心思想是對象關系映射,ORM是將表與表之間的操作,映射成對象和對象之間的操作,就是通過操作實體類來達到操作表的目的。從數據庫提取的數據會自動按你設置的映射要求封裝成特定的對象。之後你就可以通過對對象進行操作來修改數據庫中的數據。這時候你面對的不是信息的碎片,而是一個形象鮮明的對象。

二,假如你喜歡原生的Sql語句,又喜歡ORM的簡單,那你一定會喜歡上Dapper,這款ROMDapper的優勢:

  1. 輕量。只有一個文件(SqlMapper.cs),編譯完成之後只有120k(好象是變胖了)
  2. 速度快。Dapper的速度接近與IDataReader,取列表的數據超過了DataTable。
  3. 支持多種數據庫。Dapper可以在所有Ado.net Providers下工作,包括sqlite, sqlce, firebird, oracle, MySQL, PostgreSQL and SQL Server
  4. 可以映射一對一,一對多,多對多等多種關系。
  5. 性能高。通過Emit反射IDataReader的序列隊列,來快速的得到和產生對象,性能不錯。
  6. 支持FrameWork2.0,3.0,3.5,4.0,4.5  

下面介紹Dapper如何使用,來進行高效開發,以下操作是編譯後在Net3.5下操作的例子,Net4.0下大部分函數有默認值,參數很簡單。

三, 為什麽要擴展Dapper?

  了解Dapper都知道,在書寫代碼時,我們還是會手動寫SQL,擴展的目的就是在完全不改變dapper源代碼和使用基礎上,進行一次封閉,達到零SQL,實現完全對象操作。

四,原生Dapper使用流程:

  0,兩種下載使用方法:

  (1),推薦下載方法(使用Nuget下載):

  ---Nuget是一個.NET平臺下的開源的項目,它是Visual Studio的擴展。在使用Visual Studio開發基於.NET Framework的應用時,Nuget能把在項目中添加、移除和更新引用的工作變得更加快捷方便。

  技術分享圖片

  ---安裝成功以後,生成一下網站,項目bin目錄下,會生成幾個Dapper文件(主要是Dapper.dll,120k)。

  (2),可以在官網上下載Dapper源代碼,即SqlMapper.cs文件,在項目中App_Code文件夾中加入這個文件,就像Ado.net中的SqlHelper一樣。

  ---源文件地址:https://github.com/StackExchange/dapper-dot-net/blob/master/Dapper%20NET40/SqlMapper.cs

  1,下面可以在項目中開始使用Dapper了

  2,連接數據庫字符串。根據不同的數據庫進行相應設置,如果是SQL,就類似下邊設置;如果是使用SQLite,則設置方法不同。

private readonly string sqlconnection =
                 "Data Source=RENFB;Initial Catalog=test;User Id=sa;Password=sa;";
//public readonly string mysqlconnectionString =
                 @"server=127.0.0.1;database=test;uid=renfb;pwd=123456;charset=‘gbk‘";

  3,獲取Sql Server的連接數據庫對象。

public SqlConnection OpenConnection()
{
SqlConnection connection = new SqlConnection(sqlconnection); //這裏sqlconnection就是數據庫連接字符串
connection.Open();
return connection;
}
//獲取MySql的連接數據庫對象。MySqlConnection
//public MySqlConnection OpenConnection()
//{
// MySqlConnection connection = new MySqlConnection(mysqlconnectionString);
// connection.Open();
// return connection;
//}

  註:如果需要換成Mysql數據庫,只用將獲得sql Server的連接數據庫對象的函數註釋掉,取消MySql的連接數據庫對象的函數的註釋,一並取消Mysql連接字符串的註釋,並修改為自己的連接信息。

  Query()方法: Query()是IDbConnection擴展方法並且重載了,從數據庫裏提取信息,並用來填充我們的業務對象模型。

  4,//先創建一個類,是數據庫的user表的模型。
  public class user
{
public int id { get; set; }
public string name { get; set; }
public string address { get; set; }
public string age { get; set; }
}
5,手寫Sql插入數據(增)

    /// <summary>
/// 手寫Sql插入數據
/// </summary>
public int InsertWithSql()
{
using (var conn = SQLiteHelper.OpenConnection()) //這裏訪問的是Sqlite數據文件,這裏OpenConnection即上邊獲取連接數據庫對象方法
{
user user=new user();
user.name = "Dapper01";
user.address = "周口";
user.age="15";
//string _sql = "INSERT INTO User(name,address,age)VALUES(‘Dapper01‘,‘周口‘,13)";
string _sql = "INSERT INTO User(name,address,age)VALUES(@name,@address,@age)";
return conn.Execute(_sql,user);
}
}

    ---如果不用Dapper,用插入一條數據需要多少代碼量(相對上邊只用了2行代碼,下邊需要用6行代碼):

  public static int insert_news(string title, string content)
{
string sql = "insert into news(title,content,addtime) values(@title,@content,@addtime)";
SQLiteParameter[] parameters =
{
SQLiteHelper.MakeSQLiteParameter("@title", DbType.String, title.Trim()),
SQLiteHelper.MakeSQLiteParameter("@content", DbType.String, content.Trim()),
SQLiteHelper.MakeSQLiteParameter("@addtime", DbType.DateTime,DateTime.Now)
};
return SQLiteHelper.ExecuteSql(sql, parameters); //調用SQLiteHelper文件中方法,執行數據庫插入
}

6,手寫Sql輸出數據(刪)

protected void Page_Load(object sender, EventArgs e)
{
user user = new user();
user.id = 15;
DeleteColumn(user);
}

//刪除一個類別(3行):
public int DeleteColumn(user user)
{
using (IDbConnection conn = SQLiteHelper.OpenConnection())
{
const string query = "delete from user where id=@id";
return conn.Execute(query, user);
}
}

  ---不用Dapper,刪除一條數據,代碼如下(4行):

  public static int del_news(string newid)
{
string sql = "delete from news where newid=@newid";
SQLiteParameter[] parameters =
{
SQLiteHelper.MakeSQLiteParameter("@newid", DbType.String, newid.Trim())
};
return SQLiteHelper.ExecuteSql(sql, parameters);
}

7,手寫Sql更新數據(改)

protected void Page_Load(object sender, EventArgs e)
{
user user = new user();
user.id = 14;
user.name = "Dapper03";
user.address = "太康";
user.age = "25";
UpdateColumn(user);
}

//更新一個類別:
public int UpdateColumn(user user)
{
using (IDbConnection conn = SQLiteHelper.OpenConnection())
{
const string query = "update user set name=@name,address=@address,age=@age where id=@id";
return conn.Execute(query, user);
}
}

8,手寫Sql查詢數據(查)

protected void Page_Load(object sender, EventArgs e)
{
user user = new user();
user.id = 14;
user=SelectColumn(user.id);
Context.Response.Write(user.name+"|"+user.address+"|"+user.age);
Context.Response.End();
}

//獲取單個user對象。
public user SelectColumn(int user_id)
{
using (IDbConnection conn = SQLiteHelper.OpenConnection())
{
const string query = "select * from user where id=@id";
return conn.Query<user>(query, new {id = user_id}).SingleOrDefault<user>(); //這裏用的是linq語法,所以必須引用System.Linq;
}
}

  ---這裏我們傳遞了一個參數給Query方法,參數可以是任何對象,其屬性在查詢中與sql的參數匹配,由於Query總是返回一個集合,我們只需調用SingleOrDefault方法,因為我們知道總是返回0或1行.

  //獲取user對象的集合。
public IEnumerable<user> SelectUsers()
{
using (IDbConnection conn = SQLiteHelper.OpenConnection())
{
const string query = "select * from user order by id asc";
return conn.Query<user>(query, null);
}
}

    protected void Page_Load(object sender, EventArgs e)
{
IEnumerable<user> list = SelectUsers();
foreach (var i in list)
{
Context.Response.Write(i.name + "|" + i.address + "|" + i.age);
Context.Response.Write("<br/>");
}
Context.Response.End();
}

五,如果想直接插入一個實體對象,Sql語句都不要了,可以在Nuget上下載Dapper的擴展包--->Dapper.SimpleCRUD安裝包。(crud即增查改刪)

  ---使用Dapper.SimpleCRUD時,兩個註意點,1是直接插入實體,類代碼要改:

  public class user
{
[Key] //主鍵值前加個key
public int id { get; set; }
public string name { get; set; }
public string address { get; set; }
public string age { get; set; }
}

  ///<summary>
///實體插入數據
///</summary>
public int? InsertWithEntity()
{
using (var conn = SQLiteHelper.OpenConnection())
{
var user = new user { name = "Dapper02", address = "周口",age="22"};
return conn.Insert(user);
}
} 

  ---2是使用sqlite數據庫時,會報錯!錯誤內容如下,因為sqlite不支持scope_identity函數,沒有這個函數:

SQL logic error or missing database
no such function: SCOPE_IDENTITY



5,就是這麽簡單,直接在例子中嵌入Sql,很容易擴展為存儲過程,可以使用別名使結果集中的列與業務對象模型(ColumnCat)的屬性對應。

//下面使用上面的集合顯示出分類。
List<ColumnCat> AllColumnCat =SelectColumnCats().ToList<ColumnCat>();
foreach (ColumnCat cat in AllColumnCat.Where(c => c.Parentid == 0))
{
Response.Write("Name==>" + cat.Name + "\t");
Response.Write("時間==>" + cat.ModifiedOn + "\t");
Response.Write("<br/>");

foreach (ColumnCat c in AllColumnCat
.Where<ColumnCat>(subColumnCat => subColumnCat.Parentid == cat.Id))
{
Response.Write("&nbsp;&nbsp;++++");
Response.Write("Name==>" + c.Name + "\t");
Response.Write("時間==>" + c.ModifiedOn + "\t");
Response.Write("<br/>");
}
}

//將一級類別和二級類別顯示在頁面上,如果使用一個遞歸,很容易實現無限級分類(你懂的)。

7,//Dapper也可以加載填充嵌套對象,考慮這樣一種情形,考慮到新聞的類別屬性,返回類別對象,
//我們創建一個Column的類
public class Column
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime ModifiedDate { get; set; }
public ColumnCat ColumnCat { get; set; }
}

//接下來我們來填充我們的業務對象。
public IList<Column> SelectColumnsWithColumnCat()
{
using (IDbConnection conn = OpenConnection())
{
const string query = "select c.Id,c.Name,c.ModifiedDate,c.ColumnCatid
,cat.id,cat.[Name],cat.ModifiedOn,cat.Parentid from [Column] as c
left outer join ColumnCat as cat on c.ColumnCatid=cat.id";
return conn.Query<Column, ColumnCat, Column>(query
, (column, columncat) => { column.ColumnCat = columncat; return column; }
, null, null, false, "Id", null, null).ToList<Column>();
}
}

  註:1,在填充嵌套對象的時候,只好執行 ToList<>方法,否則回報ExecuteReader 要求已打開且可用的連接。連接的當前狀態為已關閉,而單個對象不會報錯,估計是using結束後關閉了連接,而嵌套對象在map的時候又執行了 ExecuteReader,只好在using結束之前返回list集合。 2,嵌套對象的參數是比較多的,主要是前兩個參數,其它參數沒用可以設置為null,不過在4.0版本可以只寫兩個參數,其它參數都有默認值。特別要註意 的是splitOn,這個參數不能為空,否則會報對象為引用的錯誤。【splitOn參數的意思是讀取第二個對象的的分割列,從哪個列起開始讀取第二個對 象,如果表裏的自增長列都為Id,可以設置這個參數為”Id”】.

  Execute方法: 正如Query方法是檢索數據的,Execute方法不會檢索數據,它與Query方法非常相似,但它總返回總數(受影響的行數),而不是一個對象集合【如:insert update和delete】.

8,//接下來向數據庫裏添加一個類別
public int InsertColumnCat(ColumnCat cat)
{
using (IDbConnection conn = OpenConnection())
{
const string query = "insert into ColumnCat([name],ModifiedOn,Parentid)
values (@name,@ModifiedOn,@Parentid)";
int row = conn.Execute(query,cat);
//更新對象的Id為數據庫裏新增的Id,假如增加之後不需要獲得新增的對象,
//只需將對象添加到數據庫裏,可以將下面的一行註釋掉。
SetIdentity(conn,id=>cat.Id=id,"id","ColumnCat");
return row;
}
}

public void SetIdentity(IDbConnection conn, Action<int> setId,string primarykey
,string tableName)
{
if (string.IsNullOrEmpty(primarykey)) primarykey = "id";
if (string.IsNullOrEmpty(tableName))
{
throw new ArgumentException("tableName參數不能為空,為查詢的表名");
}
string query = string.Format("SELECT max({0}) as Id FROM {1}", primarykey
, tableName);
NewId identity = conn.Query<NewId>(query, null).Single<NewId>();
setId(identity.Id);
}

public class NewId
{
public int Id { get; set; }
}

  由於Dapper是通過類的屬性自動綁定的,所以增 加了NewId類來獲取增加對象後的Id,本來打算使用@@identity,Net3.5下使用總是報錯,只好使用Max函數獲取。當然如果不需要獲得 更新後的對象的ID,可以不使用SetIdentity,這個函數通用。

編譯Dapper源碼生成的是Net4.0下使用的,可以借助Net4.0新增的dynamic動態類型,
//SetIdentity的實現將非常方便。如下:
public void SetIdentity<T>(IDbConnection conn, Action<int> setId)
{
dynamic identity = connection.Query("SELECT @@IDENTITY AS Id").Single();
T newId = (T)identity.Id;
setId(newId);
}
9,下面介紹一下Dapper的高級用法
//Dapper對事務處理的例子,如刪除類別的同時刪除類別下的所有新聞。或者刪除產品的同時,
//刪除產品圖片表裏關聯的所有圖片。
public int DeleteColumnCatAndColumn(ColumnCat cat)
{
using (IDbConnection conn = OpenConnection())
{
const string deleteColumn = "delete from [Column] where ColumnCatid=@catid";
const string deleteColumnCat = "delete from ColumnCat where id=@Id";

IDbTransaction transaction = conn.BeginTransaction();
int row=conn.Execute(deleteColumn, new { catid =cat.Id},transaction,null,null);
row += conn.Execute(deleteColumnCat, new { id=cat.Id},transaction,null,null);
transaction.Commit();
return row;
}
}

ORM框架之------Dapper,Net下無敵的ORM