Fluent Data 入門
由 Primates 根據互聯網資源整理FluentData 是微型 ORM(micro-ORM)家族的一名新成員,旨在比大型 ORM(full ORM)更加易用。FluentData 於本月推出,它使用 fluent API 並支持 SQL Server、SQL Azure、Oracle和 MYSQL。
FluentData 的設計者 Lars-Erik Kindblad 談到:
當前市面上的 ORM 框架,如 Entity Framework 和 NHibernate,都過於複雜而且難於學習。此外,由於這些框架自身抽象的查詢語言以及從資料庫到.NET 對象的映射太過麻煩,導致它們生成的 SQL 都很低效。FluentData 另闢蹊徑,它是一個輕量級框架,擁有簡單的 fluent API 並且很容易學會。與其他微型 ORM(如 Dapper 和 Massive)類似,FluentData 關註性能和易用性。它允許開發人員擁有對 SQL 較多的控制,而不是依賴 ORM 進行自動生成。它不僅可以使用 SQL 來執行查詢、增添和更新操作,還可以支持使用存儲過程和事務。根據文檔描述,FluentData 可以在不改動已有結構的情況下,與任何業務對象一同工作。
以下是 FluentData 的一些其他特性:
- 多結果集(Multiple Result Set):在一次資料庫操作下返回多個數據集;
- 開發人員可使用強類型對象或動態對象;
- 可為創建時需要特殊處理的複雜對象自定義實體工廠(Custom Entity Factory);
- 具有添加其他資料庫支持的能力。
FluentData 需要 .NET 4.0,並支持 SQL Server、SQL Azure、SQL Server Compact 以及使用 .NET 驅動的 Oracle 和 MySQL。 想要瞭解進一步信息,如代碼示例和免費下載,請訪問CodePlex 站點上的 FluentData
二 創建 DbContext
初始化一個 DbContext
可以在*.config 文件中配置 connection string,將 connection string name 或者將整個 connection string作為參數傳遞給 DbContext 來創建 DbContext。
重要配置
IgnoreIfAutoMapFails – IDbContext.IgnoreIfAutoMapFails 返回一個 IDbContext,該實例中,如果自動映射失敗時是否拋出異常
通過*.config 中配置的 ConnectionStringName 創建一個 DbContext
1: public IDbContext Context()
2: {
3: return new DbContext().ConnectionStringName("MyDatabase",
4: new SqlServerProvider());
5: }
調用 DbContext 的 ConnectionString 方法顯示設置 connection string 來創建
1: public IDbContext Context()
2: {
3: return new DbContext().ConnectionString(
4: "Server=MyServerAddress;Database=MyDatabase;Trusted_Connection=True;", new SqlServerProvider());
5: }
其他可以使用的 Provider
只有通過使用不同的 Provider,就可以切換到不同類型的資料庫伺服器上,比如
AccessProvider, DB2Provider, OracleProvider, MySqlProvider, PostgreSqlProvider, SqliteProvider,SqlServerCompact, SqlAzureProvider, SqlServerProvider。
三 查詢(Query)
返回單個對象
返回一個 dynamic 對象
1: dynamic product = Context.Sql(@"select * from Product where ProductId = 1").QuerySingle<dynamic>();
返回一個強類型對象:
1: Product product = Context.Sql(@"select * from Product where ProductId = 1").QuerySingle<Product>();
返回一個 DataTable:
1: DataTable products = Context.Sql("select * from Product").QuerySingle<DataTable>();
其 實 QueryMany<DataTable> 和 QuerySingle<DataTable> 都 可 以 用 來 返 回 DataTable , 但 考 慮 到QueryMany<DataTable>返回的是List<DataTable>,所以使用 QuerySingle<DataTable>來返回 DataTable 更方便。
查詢一個標量值
1: int numberOfProducts = Context.Sql(@"select count(*) from Product").QuerySingle<int>();
返回一組數據
返回一組 dynamic 對象(new in .NET 4.0)
1: List<dynamic> products = Context.Sql("select * from Product").QueryMany<dynamic>();
返回一組強類型對象
1: List<Product> products = Context.Sql("select * from Product").QueryMany<Product>();
返回一個自定義的 Collection
1: ProductionCollection products = Context.Sql("select * from Product").QueryMany<Product,ProductionCollection>();
返回一組標量值
1: List<int> productIds = Context.Sql(@"select ProductId from Product").QueryMany<int>();
參數化查詢
索引順序形式的參數
1: dynamic products = Context.Sql(@"select * from Product where ProductId = @0 or ProductId = @1", 1,
2).QueryMany<dynamic>();
或者
1: dynamic products = Context.Sql(@"select * from Product where ProductId = @0 or ProductId =
@1").Parameters(1, 2).QueryMany<dynamic>();
名字形式的參數:
1: var command = Context.Sql(@"select @ProductName = Name from Product where ProductId=1")
2: .ParameterOut("ProductName", DataTypes.String, 100);
3: command.Execute();
4: string productName = command.ParameterValue<string>("ProductName");
5: List of parameters - in operator:
6: List<int> ids = new List<int>() { 1, 2, 3, 4 };
7: dynamic products = Context.Sql(@"select * from Product
8: where ProductId in(@0)", ids).QueryMany<dynamic>();
Output 參數:
1: dynamic products = Context.Sql(@"select * from Product
2: where ProductId = @ProductId1 or ProductId = @ProductId2")
3: .Parameter("ProductId1", 1)
4: .Parameter("ProductId2", 2)
5: .QueryMany<dynamic>();
四 映射(Mapping)
自動映射
在資料庫對象和.Net object 自動進行1:1匹配
1: List<Product> products = Context.Sql(@"select * from Product").QueryMany<Product>();
自動映射到一個自定義的 Collection:
1: ProductionCollection products = Context.Sql("select * from Product").QueryMany<Product,ProductionCollection>();
如果資料庫欄位和 POCO 類屬性名不一致,使用 SQL 別名語法 AS:
1: List<Product> products = Context.Sql(@"select p.*,
2: c.CategoryId as Category_CategoryId,
3: c.Name as Category_Name
4: from Product p
5: inner join Category c on p.CategoryId = c.CategoryId")
6: .QueryMany<Product>();
在這裡 p.*中的 ProductId 和 ProductName 會自動映射到 Prodoct.ProductId 和 Product.ProductName,而Category_CategoryId 和Category_Name 將 映 射 到Product.Category.CategoryId 和Product.Category.Name.
使用 dynamic 自定義映射規則
1: List<Product> products = Context.Sql(@"select * from Product")
2: .QueryMany<Product>(Custom_mapper_using_dynamic);
3:
4: public void Custom_mapper_using_dynamic(Product product, dynamic row)
5: {
6: product.ProductId = row.ProductId;
7: product.Name = row.Name;
8: }
使用 datareader 進行自定義映射:
1: List<Product> products = Context.Sql(@"select * from Product")
2:.QueryMany<Product>(Custom_mapper_using_datareader);
4: public void Custom_mapper_using_datareader(Product product, IDataReader row)
5: {
6: product.ProductId = row.GetInt32("ProductId");
7: product.Name = row.GetString("Name");
8: }
或者,當你需要映射到一個複合類型時,可以使用 QueryComplexMany 或者 QueryComplexSingle。
1: var products = new List<Product>();
2: Context.Sql("select * from Product").QueryComplexMany<Product>(products, MapComplexProduct);
3:
4: private void MapComplexProduct(IList<Product> products, IDataReader reader)
5: {
6: var product = new Product();
7: product.ProductId = reader.GetInt32("ProductId");
8: product.Name = reader.GetString("Name");
9: products.Add(product);
10: }
五 多結果集
FluentData 支持多結果集。也就是說,可以在一次資料庫查詢中返回多個查詢結果。使用該特性的時候,記得使用類似下麵的語句對查詢語句進行包裝。需要在查詢結束後把連接關閉。
1: using (var command = Context.MultiResultSql)
2: {
3: List<Category> categories = command.Sql(
4: @"select * from Category;
5: select * from Product;").QueryMany<Category>();
6:
7: List<Product> products = command.QueryMany<Product>();
8: }
執行第一個查詢時,會從資料庫取回數據,執行第二個查詢的時候,FluentData 可以判斷出這是一個多結果集查詢,所以會直接從第一個查詢里獲取需要的數據.
六 分頁
1: List<Product> products = Context.Select<Product>("p.*, c.Name as Category_Name")
2: .From(@"Product p
3: inner join Category c on c.CategoryId = p.CategoryId")
4: .Where("p.ProductId > 0 and p.Name is not null")
5: .OrderBy("p.Name")
6: .Paging(1, 10).QueryMany();
調用 Paging(1, 10),會返回最先檢索到的10個 Product
七 資料庫操作(Insert, Update, Delete)
插入數據
使用 SQL 語句:
1: int productId = Context.Sql(@"insert into Product(Name, CategoryId)
2: values(@0, @1);")
3: .Parameters("The Warren Buffet Way", 1)
4: .ExecuteReturnLastId<int>();
使用 builder:
1: int productId = Context.Insert("Product")
2: .Column("Name", "The Warren Buffet Way")
3: .Column("CategoryId", 1)
4: .ExecuteReturnLastId<int>();
使用 builder,並且自動映射
1: Product product = new Product();
2: product.Name = "The Warren Buffet Way";
3: product.CategoryId = 1;
4:
5: product.ProductId = Context.Insert<Product>("Product", product)
6: .AutoMap(x => x.ProductId)
7: .ExecuteReturnLastId<int>();
將 ProductId 作為 AutoMap 方法的參數,是要指明 ProductId 不需要進行映射,因為它是一個資料庫自增長欄位。
更新數據
使用 SQL 語句:
1: int rowsAffected = Context.Sql(@"update Product set Name = @0 where ProductId = @1")
2: .Parameters("The Warren Buffet Way", 1)
3: .Execute();
使用 builder:
1: int rowsAffected = Context.Update("Product")
2: .Column("Name", "The Warren Buffet Way")
3: .Where("ProductId", 1)
4: .Execute();
使用 builder,並且自動映射:
1: Product product = Context.Sql(@"select * from Product
2: where ProductId = 1")
3: .QuerySingle<Product>();
4: product.Name = "The Warren Buffet Way";
5:
6: int rowsAffected = Context.Update<Product>("Product", product)
7: .AutoMap(x => x.ProductId)
8: .Where(x => x.ProductId)
9: .Execute();
將 ProductId 作為 AutoMap 方法的參數,是要指明 ProductId 不需要進行映射,因為它不需要被更新。
Insert and update - common Fill method
1: var product = new Product();
2: product.Name = "The Warren Buffet Way";
3: product.CategoryId = 1;
4:
5: var insertBuilder = Context.Insert<Product>("Product", product).Fill(FillBuilder);
6:
7: var updateBuilder = Context.Update<Product>("Product", product).Fill(FillBuilder);
8:
9: public void FillBuilder(IInsertUpdateBuilder<Product> builder)
10: {
11: builder.Column(x => x.Name);
12: builder.Column(x => x.CategoryId);
13: }
14:
15: Delete
刪除數據
使用 SQL 語句:
1: int rowsAffected = Context.Sql(@"delete from Product
2: where ProductId = 1")
3: .Execute();
使用 builder:
1: int rowsAffected = Context.Delete("Product").Where("ProductId", 1)
2: .Execute();
八存儲過程和事物
存儲過程
使用 SQL 語句:
1: var rowsAffected = Context.Sql("ProductUpdate")
2: .CommandType(DbCommandTypes.StoredProcedure)
3: . Parameter("ProductId", 1)
4: .Parameter("Name", "The Warren Buffet Way")
5: .Execute();
使用 builder:
1: var rowsAffected = Context.StoredProcedure("ProductUpdate")
2: .Parameter("Name", "The Warren Buffet Way")
3: .Parameter("ProductId", 1).Execute();
使用 builder,並且自動映射
1: var product = Context.Sql("select * from Product where ProductId = 1")
2: .QuerySingle<Product>();
3:
4: product.Name = "The Warren Buffet Way";
5:
6: var rowsAffected = Context.StoredProcedure<Product>("ProductUpdate", product)
7: .AutoMap(x => x.CategoryId).Execute();
使用 Lambda 表達式
1: var product = Context.Sql("select * from Product where ProductId = 1")
2: .QuerySingle<Product>();
3: product.Name = "The Warren Buffet Way";
4:
5: var rowsAffected = Context.StoredProcedure<Product>("ProductUpdate", product)
6: .Parameter(x => x.ProductId)
7: .Parameter(x => x.Name).Execute();
事務
FluentData 支持事務。如果使用事務,最好使用 using 語句將代碼包起來,已保證連接會被關閉。預設的,如果查詢過程發生異常,如事務不會被提交,會進行回滾。
1: using (var context = Context.UseTransaction(true))
2: {
3: context.Sql("update Product set Name = @0 where ProductId = @1")
4: .Parameters("The Warren Buffet Way", 1)
5: .Execute();
6:
7: context.Sql("update Product set Name = @0 where ProductId = @1")
8: .Parameters("Bill Gates Bio", 2)
9: .Execute();
10:
11: context.Commit();
12: }
13:
實體工廠
實體工廠負責在自動映射的時候生成 POCO 實例。如果需要生成複雜的實例,可以自定義實體工廠:
1: List<Product> products = Context.EntityFactory(new CustomEntityFactory())
2: .Sql("select * from Product")
3: .QueryMany<Product>();
4:
5: public class CustomEntityFactory : IEntityFactory
6: {
7: public virtual object Resolve(Type type)
8: {
9: return Activator.CreateInstance(type);
10: }
11: }