Aoite 系列(01) - 比 Dapper 更好用的 ORM


Aoite 是一個適於任何 .Net Framework 4.0+ 項目的快速開發整體解決方案。Aoite.Data 適用於市面上大多數的數據庫提供程序,通過統一封裝,可以在日常開發中簡單便捷的操作數據庫。

【Aoite 系列 目錄】

沒有使用 Aoite 之前是這樣的:

苦逼

當你使用 Aoite 以后是這樣的:

爽

hah~~~你還不趕緊加入 Aoite GitHub 的大家庭吧!!

1. 快速入門

本小節將會使用 Microsoft SQL Server Compact 數據庫作,一步一步的講解針對數據庫的增刪改查。

1.1 首先,我們需要創建一個實體

class SimpleUser
{
    [Column(true /* isPrimaryKey */)]
    public string Username { get; set; }
    public string Password { get; set; }
    public string Memo { get; set; }

    public override string ToString()
    {
        return "Username:{0}\tPassword:{1}\tMemo:{2}".Fmt(this.Username, this.Password, this.Memo ?? "<NULL>");
    }
}

實體的主鍵必須通過 Aoite.Data.ColumnAttribute 指定,若找不到此特性,框架還會嘗試找到 Id 屬性。若以上條件均不滿足,操作過程中便可能直接拋出錯誤。

1.2 創建一個數據庫引擎

var dbPath = GA.FullPath("Sample01.sdf");
if(File.Exists(dbPath)) File.Delete(dbPath);

MsSqlCeEngine engine = new MsSqlCeEngine(dbPath, null /* password*/);
engine.CreateDatabase();

1.3 通過代碼,創建一張表

engine.Execute(@"CREATE TABLE [SimpleUser]"
    + "([Username] nvarchar(50) NOT NULL PRIMARY KEY"
    + ", [Password] nvarchar(50) NOT NULL"
    + ", [Memo] nvarchar(500))")
    .ToNonQuery()           /* 執行查詢命令,並返回受影響的行數結果。 */
    .ThrowIfFailded();      /* 如果這個 SQL 語句出現異常,則直接拋出。 */

ToNonQuery:方法的內部原理是執行 DbCommand.ExecuteNonQuery,一般的新增、刪除、修改等操作均可用此方法。

ThrowIfFailded:任何方法的調用都會有一個 Result(結果),而這個結果包含了 Value(值)、Status(狀態碼)和Message(消息)。ThrowIfFaildedUnsafeValue 的作用一致,在結果捕獲到錯誤時,將會直接拋出異常。

通常定義 Result 的方法里,是不會直接拋出業務邏輯上的異常。方法的內部自然會包裝好的這個異常,再返回給調用方。也就是說,返回的數據類型若是繼承 System.Result,表示這個方法具有業務容錯性

1.4 基本工作已完成。現在我們試着往數據庫添加一條新紀錄

engine.Add(new SimpleUser { Username = "admin", Password = "123456" })
      .ThrowIfFailded();

Console.WriteLine("Add -> {0}", engine.FindOne<SimpleUser>("admin").UnsafeValue);

Add:這是一個擴展方法,通過高性能的反射,將對應的實體生成 INSERT 語句。

FindOne:這也是一個擴展方法,默認情況下是根據 Primary Key 去查找一條記錄。

對於添加對象,還要介紹一種非常好用的方式,匿名方式:

engine.AddAnonymous<SimpleUser>(new { Username = "admin", Password = "123456" })
      .ThrowIfFailded();

這樣兩種添加方式有什么區別嗎?有!第一個生成的是完整的 INSERT 語句(包括 Simple.Memo),而 AddAnonymous 的 INSERT 語句不含 Simple.Memo

1.5 通過匿名方式,你還可以這樣修改對象

engine.ModifyAnonymous<SimpleUser>(new { Username = "admin", Memo = "haha" })
      .ThrowIfFailded();

匿名方式用於非常多的場景,最常見的比如:修改用戶的密碼,如果有 Modify,那么表里的這條記錄所有數據都會被覆蓋,而通過 ModifyAnonymous 方法,可以簡單的 new { Id = id, Password = passowrd } 來生成更加簡短的 UPDATE 語句。

同樣,刪除的時候,可以通過 RemoveAnonymous 刪除指定主鍵的記錄。

engine.RemoveAnonymous<SimpleUser>("admin")
      .ThrowIfFailded();

你看,Aoite.Data 就是這么任性、好用、簡單</span/>。

2. 上下文

顯然,Aoite.Data 肯定不僅僅只有上個小節的那些內容。

上下文是 Aoite.Data 一個重要的概念。上個小節的所有執行的生命周期是這樣的:

![簡單連接的生命周期](http://images.cnblogs.com/cnblogs_com/sofire/656252/o_Sofire.Data simple execute.png)

倘若采用以下代碼進行數據庫操作,將會極大的浪費數據庫資源:

var r1 = engine.Execute("sql1.....").ToNonQuery();
var r2 = engine.Execute("sql2.....").ToNonQuery();
var r3 = engine.Execute("sql3.....").ToNonQuery();
var r4 = engine.Execute("sql4.....").ToNonQuery();

顯然,這種方式弱爆了。對於有嚴重開發潔癖的我們,怎能忍受這種代碼呢?所以,上下文的目的是實現一次打開連接,多次執行,最后關閉連接,從而達到節省數據庫連接池資源。可以通過以下方式:

using(var context = engine.Context)
{
    //- context.Open(); 此句可以節省,在第一次執行命令時,如果連接還未打開,將會自動打開連接。
    var r1 = context.Execute("sql1.....").ToNonQuery();
    var r2 = context.Execute("sql2.....").ToNonQuery();
    var r3 = context.Execute("sql3.....").ToNonQuery();
    var r4 = context.Execute("sql4.....").ToNonQuery();
    //- using 釋放時,將會自動關閉,從而保證無論在哪一種情況(哪里系統崩潰)下都可以關閉連接。
}

開啟事務有三種方式:

  • context.OpenTransaction():手工開啟事務。
  • using (var context = engine.ContextTransaction):指定當前范圍內都是采用事務。
  • System.Transactions.TransactionScope

當然了,最重要的是,事務的結束之前,我們必須調用 context.Commit() 來提交事務,否則整個事務將會被丟棄。若希望手工回滾的話,可以調用 context.Rollback()

![上下文生命周期](http://images.cnblogs.com/cnblogs_com/sofire/656252/o_Sofire.Data context execute.png)

示例

我們緊接着前面的示例代碼,采用上下文批量添加 10 條記錄:

using(var context = engine.Context)
{
    for(int i = 0; i < 10; i++)
    {
        context.Add(new SimpleUser { Username = "user" + i, Password = "123456" })
               .ThrowIfFailded();
        Console.WriteLine("數據庫 RowCount -> {0}", engine.RowCount<SimpleUser>());
        Console.WriteLine("上下文 RowCount -> {0}", context.RowCount<SimpleUser>());
    }
}

上方代碼輸出如下所示:

數據庫 RowCount -> 1
上下文 RowCount -> 1
數據庫 RowCount -> 2
上下文 RowCount -> 2
數據庫 RowCount -> ...
上下文 RowCount -> ...
數據庫 RowCount -> 10
上下文 RowCount -> 10

我們來試着查詢所有記錄:

foreach(var item in engine.FindAllWhere<SimpleUser>().UnsafeValue)
{
    Console.WriteLine(item);
}

FindAllWhere:根據指定篩選條件,查詢指定泛型表的所有記錄。

而采用事務的代碼:

using(var context = engine.ContextTransaction)
{
    for(int i = 10; i < 20; i++)
    {
        context.Add(new SimpleUser { Username = "user" + i, Password = "123456" })
               .ThrowIfFailded();
        Console.WriteLine("數據庫 RowCOUNT -> {0}", engine.RowCount<SimpleUser>());
        Console.WriteLine("上下文 RowCOUNT -> {0}", context.RowCount<SimpleUser>());
    }
    Console.WriteLine("事務提交前 數據庫 RowCOUNT -> {0}", engine.RowCount<SimpleUser>());
    context.Commit().ThrowIfFailded();
    Console.WriteLine("事務提交后 數據庫 RowCOUNT -> {0}", engine.RowCount<SimpleUser>());
}

以上代碼的輸出:

使用事務上下文...
數據庫 RowCOUNT -> 10
上下文 RowCOUNT -> 11
數據庫 RowCOUNT -> 10
上下文 RowCOUNT -> 12
數據庫 RowCOUNT -> 10
上下文 RowCOUNT -> ...
數據庫 RowCOUNT -> 10
上下文 RowCOUNT -> 20
事務提交前 數據庫 RowCOUNT -> 10
事務提交后 數據庫 RowCOUNT -> 20

3. 結束

關於 Aoite.Data 的簡單介紹,就到此結束了,如果你喜歡這個框架,不妨點個推薦吧!如果你非常喜歡這個框架,那請順便到Aoite GitHub Star 一下 :)

點此下載本文的所有示例代碼。

問答:

  1. 是否開源:當然
  2. 單元測試:當然。覆蓋率沒達到 100%,但基本上 80% 的邏輯都有了單元測試。
  3. 有商用項目案例嗎:!如:亞XXX得綜合管理系統、亞XXX得門店系統、短信刪除助手、XX通訊錄(擦,這個名字我忘了)、店X寶虛擬充值平台、店X寶O2O商城、遛XXX電子商城……很多。
  4. 為什么現在不支持 ORACLE 或 Mysql 等數據庫:支持。具體可參考 Aoite.Data 的前身 Sofire.Data。只不過現在為了將 Aoite.dll 瘦身,這些功能可以通過簡單擴展可以快速的實現……


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM