PetaPoco是一款適用於.Net 和Mono的微小、快速、單文件的微型ORM。
PetaPoco有以下特色:
- 微小,沒有依賴項……單個的C#文件可以方便的添加到任何項目中。
- 工作於嚴格的沒有裝飾的Poco類,和幾乎全部加了特性的Poco類
- Insert/Delete/Update/Save and IsNew 等幫助方法。
- 分頁支持:自動得到總行數和數據
- 支持簡單的事務
- 更好的支持參數替換,包括從對象屬性中抓取命名的參數。
- 很好的性能,剔除了Linq,並通過Dynamic方法快速的為屬性賦值
- T4模板自動生成Poco類
- 查詢語言是Sql……不支持別扭的fluent或Linq語法(仁者見仁,智者見智)
- 包含一個低耦合的Sql Builder類,讓內聯的Sql更容易書寫
- 為異常信息記錄、值轉換器安裝和數據映射提供鈎子。(Hooks for logging exceptions, installing value converters and mapping columns to properties without attributes.)
- 兼容SQL Server, SQL Server CE, MySQL, PostgreSQL and Oracle。
- 可以在.NET 3.5 或Mono 2.6或更高版本上運行
- 在.NET 4.0 和Mono 2.8下支持dynamic
- NUnit單元測試
- 開源(Apache License)
- 所有功能大約用了1500行代碼
可以從這里獲得PetaPoco:
代碼展示:
首先,定義一個Poco類:
// Represents a record in the "articles" table public class article { public long article_id { get; set; } public string title { get; set; } public DateTime date_created { get; set; } public bool draft { get; set; } public string content { get; set; } }
接下來,創建一個PetaPoco.Database,來執行查詢:
// Create a PetaPoco database object var db=new PetaPoco.Database("connectionStringName"); // Show all articles foreach (var a in db.Query<article>("SELECT * FROM articles")) { Console.WriteLine("{0} - {1}", a.article_id, a.title); }
得到一個scalar:
long count=db.ExecuteScalar<long>("SELECT Count(*) FROM articles");
得到一行記錄:
var a = db.SingleOrDefault<article>("SELECT * FROM articles WHERE article_id=@0", 123));
獲取分頁數據:
PetaPoco能夠自動完成分頁請求
var result=db.Page<article>(1, 20, // <-- page number and items per page "SELECT * FROM articles WHERE category=@0 ORDER BY date_posted DESC", "coolstuff");
你將會得到一個PagedFetch對象:
public class Page<T> where T:new() { public long CurrentPage { get; set; } public long ItemsPerPage { get; set; } public long TotalPages { get; set; } public long TotalItems { get; set; } public List<T> Items { get; set; } }
PetaPoco在背后為我們做了一下處理:
- 生成並執行一個查詢,得到匹配的數據行數
- 修改原始的查詢語句,使其只得到所有匹配數據的一個子集
你現在已經擁有了一個展示單頁數據的一切東西和一個分頁控制器,他們被封裝在一個現成的小對象中。(You now have everything to display a page of data and a pager control all wrapped up in one handy little object!)
Query vs Fetch
Database 對象有兩個獲取數據的方法:Query 和Fetch。這兩個方法非常相似,不同的是Fetch方法返回一個POCO類的List<>,而Query使用 yield return 迭代所有數據,這些數據並沒有加載到內存中。
不帶查詢的命令
使用Execute 方法執行一個不帶查詢的命令:
db.Execute("DELETE FROM articles WHERE draft<>0");
Inserts、Updates 和 Deletes
PetaPoco提供了insert、update和delete操作的幫助。
在插入一條記錄時,你需要指定插入的表名和主鍵:
// Create the article var a=new article(); a.title="My new article"; a.content="PetaPoco was here"; a.date_created=DateTime.UtcNow; // Insert it db.Insert("articles", "article_id", a); // by now a.article_id will have the id of the new article
更新記錄也一樣:
// Get a record var a=db.SingleOrDefault<article>("SELECT * FROM articles WHERE article_id=@0", 123); // Change it a.content="PetaPoco was here again"; // Save it db.Update("articles", "article_id", a);
或者你可以傳一個匿名類來更新一部分字段。下面的代碼只更新article的title字段:
db.Update("articles", "article_id", new { title="New title" }, 123);
刪除:
// Delete an article extracting the primary key from a record db.Delete("articles", "article_id", a); // Or if you already have the ID elsewhere db.Delete("articles", "article_id", null, 123);
修飾POCO類
在上面的例子中,必須指明表名和主鍵是很煩人的,你可以在你的Poco類中附加這些信息:
// Represents a record in the "articles" table [PetaPoco.TableName("articles")] [PetaPoco.PrimaryKey("article_id")] public class article { public long article_id { get; set; } public string title { get; set; } public DateTime date_created { get; set; } public bool draft { get; set; } public string content { get; set; } }
簡化后的insert、update、delete:
// Insert a record var a=new article(); a.title="My new article"; a.content="PetaPoco was here"; a.date_created=DateTime.UtcNow; db.Insert(a); // Update it a.content="Blah blah"; db.Update(a); // Delete it db.Delete(a);
delete和update的其它方式
// Delete an article db.Delete<article>("WHERE article_id=@0", 123); // Update an article db.Update<article>("SET title=@0 WHERE article_id=@1", "New Title", 123);
你還可以告訴POCO忽略某列
public class article { [PetaPoco.Ignore] public long SomeCalculatedFieldPerhaps { get; set; } }
或許你喜歡一點更詳細的描述。和自動映射所有列相比,你可以通過使用類和列的屬性來指明哪些列需要映射。
// Represents a record in the "articles" table [PetaPoco.TableName("articles")] [PetaPoco.PrimaryKey("article_id")] [PetaPoco.ExplicitColumns] public class article { [PetaPoco.Column]publiclong article_id { get; set;} [PetaPoco.Column]publicstring title { get; set;} [PetaPoco.Column]publicDateTime date_created { get; set;} [PetaPoco.Column]public bool draft { get; set;} [PetaPoco.Column]publicstring content { get; set;} }
它可以結合partial class 很好的工作,把需要綁定的字段放在一個.cs文件中,把計算得到的和別的有用的屬性添加到分開的文件中,而不用去考慮DAL。
Hey!是不是已經有裝飾POCO數據庫的標准屬性了呢?
好吧,PetaPoco僅支持少數幾個,因為我不想引起混亂。
Hey!稍等……它們不是POCO對象了!
當然,它們打破了嚴格的POCO概念,但使用它們可以讓POCO更容易工作。
T4 模板
Writing all those POCO objects can soon get tedious and error prone... so PetaPoco includes a T4 template that can automatically write classes for all the tables in your your SQL Server, SQL Server CE, MySQL, PostgreSQL or Oracle database.
Using the T4 template is pretty simple. The git repository includes three files (The NuGet package adds these to your project automatically in the folder \Models\Generated).
- PetaPoco.Core.ttinclude - includes all the helper routines for reading the DB schema
- PetaPoco.Generator.ttinclude - the actual template that defines what's generated
- Database.tt - the template itself that includes various settings and includes the two other ttinclude files.
A typical Database.tt file looks like this:
<#@ include file="PetaPoco.Core.ttinclude" #>
<#
// Settings
ConnectionStringName="jab";
Namespace=ConnectionStringName;
DatabaseName=ConnectionStringName;
stringRepoName=DatabaseName+"DB";
bool GenerateOperations=true;
// Load tables
var tables =LoadTables();
#>
<#@ include file="PetaPoco.Generator.ttinclude" #>
To use the template:
- Add the three files to you C# project
- Make sure you have a connection string and provider name set in your app.config or web.config file
- Edit ConnectionStringName property in Records.tt (ie: change it from "jab" to the name of your connection string)
- Save Database.tt.
All going well Database.cs should be generated with POCO objects representing all the tables in your database. To get the project to build you'll also need to add PetaPoco.cs to your project and ensure it is set to compile (NuGet does this for you) .
The template is based on the SubSonic template. If you're familiar with its ActiveRecord templates you'll find PetaPoco's template very similar.
自動的Select語句
當使用PetaPoco時,大多數查詢都以"SELECT * FROM table"開頭。鑒於我們現在可以從POCO對象的attribute中得到表名,我們沒有理由不自動生成Select語句。
如果你運行一個不以select開頭的查詢, PetaPoco會自動的將它加上:
// Get a record var a=db.SingleOrDefault<article>("SELECT * FROM articles WHERE article_id=@0",123);
可以簡寫:
// Get a record
var a=db.SingleOrDefault<article>("WHERE article_id=@0",123);
PetaPoco實際上並不生成"SELECT *",它更准確的得到要查詢的列名。
IsNew 和Save 方法
有時你有一個POCO,你想要知道數據庫中是否已經存在。因為我們有主鍵,我們所要做的檢查是該屬性被設置為別的值還是默認值。
檢測是否為新增:
// Is this a new record
if(db.IsNew(a))
{
// Yes it is...
}
和它相關聯還有一個Save方法,它將根據判斷的結果執行Insert或Update。
// Save a new or existing record
db.Save(a);
事務
事務相當的簡單:
using (var scope=db.Transaction)
{
// Do transacted updates here
// Commit
scope.Complete();
}
事務可以是嵌套的,因此你可以調用其它包含事務的方法,或者被包含在單個的事務中。當所有事務都執行完成了,事務將會提交,否則所有操作都將回滾。
注意:為了使用事務,所有操作都需要相同的PetaPoco Database對象實例。你很可能想到IOC容器中為在每一個http請求或每一個線程共享同一個實體,我喜歡用StructureMap。
Linq從哪兒實現?
沒有任何支持。我在Subsonic中使用Linq很長時間,我發現自己下降到使用CodingHorror 來做這些事情,因為:
- 不能用簡單的Linq實現
- 在.NET 下工作,但不支持Mono(尤其是Mono 2.6)
- 低效。例如:Subsonic 中activerecord.SingleOrDefault(x=x.id==123)的效率比CodingHorror 低20倍。
Now that I've got CodingHorror all over the place it bugs me that half the code is Linq and half is SQL.
Also, I've realized that for me the most annoying thing about SQL directly in the code is not the fact that it's SQL but that it's nasty to format nicely and to build up those SQL strings.
So...
PetaPoco's SQL Builder
目前已經有很多構建SQL的API,以下是我的版本,它確實很基礎!
我的目標是格式化SQL更簡單,並且通過適當的參數替換達到防止SQL注入的作用。這不能保證SQL語法的正確,也不支持使用intellisense。
以下是非常基礎的寫法:
var id=123;
var a=db.Query<article>(PetaPoco.Sql.Builder
.Append("SELECT * FROM articles")
.Append("WHERE article_id=@0", id)
)
很管用吧,參數索引器來通過調用.Append是多么酷啊!【Big deal right? Well what's cool about this is that the parameter indicies are specific to each .Append call:】
var id=123;
var a=db.Query<article>(PetaPoco.Sql.Builder
.Append("SELECT * FROM articles")
.Append("WHERE article_id=@0", id)
.Append("AND date_created<@0",DateTime.UtcNow)
)
你也可以根據條件構建SQL:
var id=123;
var sql=PetaPoco.Sql.Builder
.Append("SELECT * FROM articles")
.Append("WHERE article_id=@0", id);
if(start_date.HasValue)
sql.Append("AND date_created>=@0", start_date.Value);
if(end_date.HasValue)
sql.Append("AND date_created<=@0", end_date.Value);
var a=db.Query<article>(sql)
注意到每個append調用都用到餐廚@0了嗎?PetaPoco構建整個列表的參數,將這些參數索引更新到內部。
你也可以使用命名參數,然后他會在傳遞的參數中找到合適的屬性名。
sql.Append("AND date_created>=@start AND date_created<=@end",
new
{
start=DateTime.UtcNow.AddDays(-2),
end=DateTime.UtcNow
}
);
不管是數字的還是命名的參數,如果任何一個參數不能被推斷出來,都會拋出一個異常。
這里還有幾個創建SQL的公用方法:
var sql=PetaPoco.Sql.Builder()
.Select("*")
.From("articles")
.Where("date_created < @0",DateTime.UtcNow)
.OrderBy("date_created DESC");
跟蹤Sql命令
有些時候能夠看到執行的Sql語句會非常有用,PetaPoco為此提供了三個屬性:
- LastSQL - 非常明顯,不解釋
- LastArgs - 傳遞的參數數組
- LastCommand - SQL語句和參數字符串
在調試器中查看LastCommand屬性能夠簡單的看到執行了那些操作!
OnException Handler Routine
PetaPoco所執行的Sql命令都封裝在try/catch語句塊中,所有的異常信息都會傳遞給OnException虛方法。通過記錄這些異常(或在這個方法中設置斷點),你可以輕松的跟蹤那些地方出現了問題。
More
上面的內容展示了最基本的PetaPoco使用方法,想了解更多,請查看這些博客內容。
原文地址:http://www.toptensoftware.com/petapoco/
