最近忙碌許久,本系列暫停更新。從今天起重啟「走進 LINQ」之旅。LINQ to SQL 核心用法已大體說明,尚有細節需補充。
首篇提過 DataContext 是 LINQ to SQL 的入口點,幾乎所有操作都施加於此類別。使用 VS 設計器產生的程式碼中,會看到一個繼承自 DataContext 的部分類別,慣稱「強型別 DataContext」,它對 DataContext 做進一步封裝。
今天詳細討論 DataContext 先前未介紹的細節。首先手寫一個強型別 DataContext:
[Database(Name="CnBlogs")]
public class CnBlogsDataContext : DataContext
{
public CnBlogsDataContext(string fileOrConnectionString) : base(fileOrConnectionString) { }
public CnBlogsDataContext(string fileOrConnectionString, MappingSource mapping) : base(fileOrConnectionString, mapping) { }
public CnBlogsDataContext(IDbConnection conn) : base(conn) { }
public CnBlogsDataContext(IDbConnection conn, MappingSource mapping) : base(conn, mapping) { }
public Table<Post> Posts { get { return this.GetTable<Post>(); } }
public Table<Blog> Blogs { get { return this.GetTable<Blog>(); } }
public Table<User> Users { get { return this.GetTable<User>(); } }
[Function(Name="dbo.GetPostsByBlogId")]
public ISingleResult<Post> GetPostsByBlogId(
[Parameter(Name="blogid", DbType="int")] int blogid)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), blogid);
return (ISingleResult<Post>)result.ReturnValue;
}
[Function(Name="dbo.GetBblogsOrPosts")]
[ResultType(typeof(Blog))]
[ResultType(typeof(Post))]
public IMultipleResults GetBlogsOrPosts(
[Parameter(Name="kind", DbType="int")] int kind)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)MethodInfo.GetCurrentMethod()), kind);
return (IMultipleResults)result.ReturnValue;
}
[Function(Name="dbo.GetBblogsAndPosts")]
[ResultType(typeof(Blog))]
[ResultType(typeof(Post))]
public IMultipleResults GetBlogsOrPosts()
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)MethodInfo.GetCurrentMethod()));
return (IMultipleResults)result.ReturnValue;
}
}
此類別出現四個新特性:Database、Function、Parameter、ResultType。Database 用於資料庫對應,不贅述。以下討論其餘三者。
LINQ to SQL 不僅支援欄位與屬性對應,還能將預存程序或使用者自訂函數與 .NET 方法對應,功能強大。此對應由 Function 與 Parameter 共同完成。
IsComposable 與 Name 兩屬性。Name 指定資料庫中預存程序或函數名稱。IsComposable=true 表示對應使用者自訂函數,預設 false 表預存程序。只能套用在方法上。預存程序:
ALTER PROCEDURE dbo.GetPostsByBlogId
(@blogid int)
AS
SELECT postid, blogid, title, body, createdate FROM posts WHERE blogid = @blogid
RETURN
.NET 方法對應:
[Function(Name="dbo.GetPostsByBlogId")]
public ISingleResult<Post> GetPostsByBlogId(
[Parameter(Name="blogid", DbType="int")] int blogid)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), blogid);
return (ISingleResult<Post>)result.ReturnValue;
}
呼叫方式:
ISingleResult<Post> posts = dbCtx.GetPostsByBlogId(1);
foreach (var p in posts) Console.WriteLine(p.Title);
預存程序:
ALTER PROCEDURE dbo.GetBblogsOrPosts
(@kind int)
AS
IF @kind = 1 SELECT * FROM blogs ELSE SELECT * FROM posts
RETURN
.NET 方法對應:
[Function(Name="dbo.GetBblogsOrPosts")]
[ResultType(typeof(Blog))]
[ResultType(typeof(Post))]
public IMultipleResults GetBlogsOrPosts(
[Parameter(Name="kind", DbType="int")] int kind)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)MethodInfo.GetCurrentMethod()), kind);
return (IMultipleResults)result.ReturnValue;
}
使用 IMultipleResults.GetResult<T>() 取得特定型別結果:
IMultipleResults results = dbCtx.GetBlogsOrPosts(1);
foreach (var b in results.GetResult<Blog>()) Console.WriteLine(b.Name);
預存程序:
CREATE PROCEDURE dbo.GetBblogsAndPosts
AS
SELECT * FROM blogs
SELECT * FROM posts
RETURN
.NET 方法對應方式同範例 2。
ExecuteCommand、ExecuteQuery<TResult>、Translate 三方法為與傳統 ADO.NET 整合而設計。
執行 INSERT、UPDATE、DELETE。第一個參數接受格式化 SQL,第二個為參數陣列:
dbCtx.ExecuteCommand("insert into posts (blogid,title,body) values({0},{1},{2})", "2", "走進Linq-How do I(4)", "廢話一篇");
執行查詢,回傳 IEnumerable<T>:
IEnumerable<Post> posts = dbCtx.ExecuteQuery<Post>("select * from posts where postid = {0}", "1");
foreach (var post in posts) Console.WriteLine(post.Title);
將傳統 DataReader 轉換為 LINQ to SQL 物件,優點:
post.Title = dr[1].ToString() 等手動指派。為提升效能或實作複雜邏輯,可覆寫預設行為。步驟:
dbo.CreatePost)。LINQ to SQL 內建支援樂觀並行交易,也可顯式啟動:
dbCtx.Transaction = dbCtx.Connection.BeginTransaction();
try
{
dbCtx.ExecuteCommand("insert into posts(blogid,title,body) values({0},{1},{2})", "2", "走進Linq-How do I(4)", "廢話一篇");
dbCtx.ExecuteCommand("insert into posts(blogid,title,body) values({0},{1},{2})", "3", "走進Linq-How do I(5)", "廢話兩篇");
dbCtx.Transaction.Commit();
}
catch
{
dbCtx.Transaction.Rollback();
}
BeginTransaction 可接受 IsolationLevel 指定隔離等級。跨多個 DataContext 的交易可使用 .NET 2.0 引入的 TransactionScope。
下一篇將討論 RowVersion 相關議題。