EF 太重,MyBatis 太輕,ORM 框架到底怎么選 ?


EF 為代表的基於 LinqORM 框架總是 很重

他們的功能早已超出了一個 ORM 的范疇,
ORM 是 Object Relational Mapping ,從名字上看,其初衷是將 數據庫中的字段 與 實體中的屬性 進行關聯映射,
但是 重型 ORM 框架 做了很多 額外 的事情 :

  • 數據庫連接
  • 數據庫事務包裝
  • 實體緩存
  • 實體關聯管理
  • 數據庫表同步
  • 這些功能很好,
  • 強大的功能往往是死板的,

我們無法編寫那些靈活的 Sql 去實現某些簡便的操作。


以 MyBatis.NET、Dapper 為代表的,
則是基於開發者自行編寫 Sql 的 ORM 框架又 太輕。

因為是自行編寫 Sql ,
所以他們非常靈活,
但是用起來很 痛苦
哪怕是一個簡單的 Insert ,Update 也得寫 Sql,

而且還無法擺脫 數據庫 兼容的問題。
你所編寫的那些 Sql 在大部分情況下,只能用於一種 數據庫


今天要向大家介紹一個 輕量級、不用寫 Sql、可以兼容多數據庫ORM 框架

Reface.NPI


什么是 NPI

NPI 全名 .Net Persistent Interface

這是一個利用 interface 實現的輕量級 ORM 框架,

它與市面上大多數的 ORM 框架不同,它不基於 Linq 進行數據庫操作,而是基於 Method Name

例如

IList<User> SelectById(int id);

IList<User> SelecyByNameLike(string name);

void UpdatePasswordById(int id, string password);

bool DeleteById(int id);

NPI 提供了將上述 MethodName實際運算時的入參 生成 Sql執行信息 的方法。

此庫不實現以下功能

  • 通過 AOP 產生 interfaceProxy ( 這個功能會在 Reface.AppStarter.NPI 中基於 Castle.DynamicProxy 實現 )
  • Sql執行信息 的執行
  • 將查詢結果映射到對應的實體中 ( 這個功能會在 Reface.AppStarter.NPI 以基於 Dapper 實現 )
  • 對事務的管理 ( 這個功能預計在一個由 Reface.AppStarter 構建的一個業務框架中實現 )

不建議直接將此庫用於業務功能的開發,
建議對該庫進行一定的二次開發或封裝后再投入使用,
開發者可以根據系統當前已經依賴的庫進行封裝。

計划在未來轉為 .NetStandard 版本,以同時提供給 .NetCore 使用。

依賴項

  • Reface.StateMachine ( 庫中對方法名稱解析的過程依賴於此庫 )
  • Reface ( 提供了一些基礎的功能和方法,使用 .NetStandard2.0 編寫 )

由於使用了 .NetStrand2.0,因此本庫需要 .Net framework 4.6.1 及以上版本才能使用。

使用方法

四個分析器

系統中針對四種數據庫不同的操作(增刪改查),分別提供了四個不同的語義分析器

  • ISelectParser
  • IInsertParser
  • IUpdateParser
  • IDeleteParser

這四種轉化器能夠將一個字符串分析成為結構化的數據庫處理結構,如

ISelectParser parser = new DefaultSelectParser();
string command = "ByIdAndName";
SelectInfo info = parser.Parse(command);
// info.Fields = [];
// info.Conditions[0].Field = "Id";
// info.Conditions[1].Field = "Name";
// info.Orders = [];

四種分析器分別能生成四種不同的語句結構:

Parser 結果的類型
ISelectParser SelectInfo
IInsertParser InsertInfo
IUpdateParser UpdateInfo
IDeleteParser DeleteInfo

這些 xxxInfo 的結構並不復雜,這里將不對其展開進行更多的介紹。

四個分析器的整合

ICommandParser 對四個分析器做了整合,以便我們不關心對方法的區分而直接得到 ICommandInfo
SelectInfo , InsertInfo , UpdateIfoDeleteInfo 都實現了 ICommandInfo 接口。

ICommandParser 通過方法的第一個單詞對方法名進行分類,哪些前綴屬於查詢、哪些前綴屬於更新,都是由它的實現的。

庫中的 DefaultCommandParser 按照下面的 首單詞 進行邏輯區分 :

查詢語句

  • Get
  • Select
  • Fetch
  • Find
  • PagingGet
  • PagingSelect
  • PagingFetch
  • PagingFind

新增語句

  • Insert
  • New
  • Create

更新語句

  • Update
  • Modify

刪除語句

  • Delete
  • Remove

下面的例子是分析了一個更新語句。
ICommandInfo 中的 Type 字段有助於你判斷應當將 ICommandInfo 轉化為一個具體的 Info

string command = "UpdateNameById";
ICommandParser parser = new DefaultCommandParser();
ICommandInfo info = parser.Parse(command);
if (info.Type == CommandInfos.Update)
    UpdateInfo updateInfo = (UpdateInfo)info;
// updateInfo.SetFields[0].Field = "Name";
// updateInfo.Conditions[0].Field = "Id";

通過方名和參數生成執行信息

執行信息包含兩個信息

  • Sql 語句
  • Sql 參數

在庫中,生成執行信息是由 ISqlCommandGenerator 完成的。

// ISqlCommandGenerator.cs
using System.Reflection;

namespace Reface.NPI.Generators
{
    public interface ISqlCommandGenerator
    {
        SqlCommandDescription Generate(MethodInfo methodInfo, object[] arguments);
    }
}

設計該接口的初衷是希望使用方是以 AOP 的方式攔截某個方法的執行,
並將 MethodInfo攔截到的入參 傳遞給 ISqlCommandGenerator
再根據生成的執行信息直接執行,得到結果。

目前庫中有一個它的實現類型 : DefaultSqlServerCommandGenerator
你會從名字上發現,它是面向 SqlServer 的實現,
很明顯,不同的 數據庫 往往支持的語句並不相同。
因此,為不同的 數據庫 編寫不同的 ISqlCommandGenerator 是有必要的。

SqlCommandDescripion 是一個簡單的數據結構,它包含 SqlCommandParameters 兩個主要的屬性,使用這兩個屬性可以完成后續的 Sql 執行。

注意事項

  • 對於表名的獲取,是基於 INpiDao<TEntity> 中的 TEntity 來完成的,
    通過反射 TEntity 上的 System.ComponentModel.DataAnnotations.Schema.TableAttibute 特征來獲取表名。
  • 由於 TableAttribute 在很多常見庫中出現,所以要注意不要引用錯了。
  • Reface.NPI 允許你重寫對表名的獲取,對字段的獲取等邏輯,重寫方法會在后面的文章中介紹。

方法名及預期 Sql 參照表

方法名稱 期望的 Sql 說明
SelectById select * from [table] where id = ? 以 Id 作為條件查詢實體
SelectNameAndAgeById select name, age from [table] where id = ? 以 Id 作為條件,只查詢 Name 和 Age 字段
SelectByRegistertimeGreaterthan select * from [table] where Registertime > ? 查詢 RegisterTime 大於參數的實體
SelectByRegistertimeGteq select * from [table] where Registertime >= ? 查詢 RegisterTime 大於等於參數的實體
SelectByIdAndName select * from [table] where id = ? and name = ? 按 Id 且 Name 作為條件查詢實體
SelectByIdOrName select * from [table] where id = ? or name = ? 以 Id 或 Name 作為條件查詢實體
SelectByIdOrNameLike select * from [table] where id = ? or name like ? 以 Id 或 Name Like 作為條件查詢實體
SelectByIdOrderbyName select * from [table] where id = ? order by name 以 Id 查詢並以 Name 排序
SelectByIdOrderByNameDesc select * from [table] where id = ? order by name desc 以 Id 作為條件並以 Name 倒序排序
DeleteById delete from [table] where id = ? 以 Id 作為條件刪除
UpdatePasswordById update [table] set password = ? where id = ? 以 Id 作為條件更新 Password
UpdatePasswordByNameLike update [table] set password = ? where name like ? 以 Name Like 作為條件更新 Password
UpdateStateAndTokenByLastoprtimeGt update [table] set state=?,token=? where lastoprtime > ? 以 LastOprTime 大於 作為條件更新 state 和 token
UpdateById update [table] set ... where id = ? 以 Id 作為條件,並以 Id 以外的字段作為 Set 子句
UpdateWithoutCreatetimeById update [table] set ... where id = ? 以 Id 作為條件,並以 Id 和 Createtime 以外的字段作為 Set 子句
UpdateWithoutStateCreatetimeById update [table] set ... where id = ? 以 Id 作為條件,並以 Id 、State 和 Createtime 以外的字段作為 Set 子句
InsertWithoutIdCreatetime(Entity) insert into [table] (...) values(?,...,?) 排除 Id 和 Createtime 字段新增實現

相關鏈接

下期預告 : 《NPI 方法規則詳解》
將詳細地說明四種操作所支持的各種操作。


免責聲明!

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



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