最近很忙,真的很忙,所以這個系列好久沒更新了,從今天起我又開始了我 的走進Linq之旅。Linq to SQL的用法基本上都說完了,還有一些細枝末節的地 方需要聊聊。
強類型DataContext
在Linq to SQL的第一篇的時候 就說道DataContext是一個入口點,我們使用Linq to SQL做的一些操作幾乎都是 施加在這個類上的。在使用VS的設計器生成的代碼裡,我們會看到一個從 DataContext繼承的局部類,大家都習慣的將這個類稱之為強類型的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就不用說了,就是做數 據庫映射的。下面對其它三個做一些討論:
Function、Parameter和 ResultType
Linq to SQL不僅僅能做字段與屬性之間的映射,還可以將存 儲過程或用戶自定義方法與.net裡的方法之間做映射,功能是不是很強大?這個 映射就是通過Function和Parameter共同完成的。
Function有兩個屬性 IsComposable和Name,Name就是用來指定數據庫中存儲過程或者用戶自定義方法 的名字,當IsComposable為true的時候,則表明該方法對應著一個用戶自定義方 法,否則對應一個存儲過程,默認是false。Function特性只能放在方法上面。
Parameter就是用來映射存儲過程或方法接受的參數。
還是用例 子來說明:
假設有這樣的一個存儲過程,通過blogid找出其所有的隨筆
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;
}
現在你可以以調用.net方法的形式直接調用這個方法來調用存儲 過程
ISingleResult<Post> posts = dbCtx.GetPostsByBlogId(1);
foreach (var p in posts)
{
Console.WriteLine(p.Title);
}
這樣就將存儲過程封裝成.NET的方法了,調用看看:
使用存儲過程的時候,我們往往使用一個條件參數,根據條件參數的 不同返回不同的結果集:
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方法:
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
使用方法還是和上面的一樣。
在DataContext還有 幾個我們比較感興趣的方法:
ExecuteCommand, ExecuteQuery<TResult>,Translate
這幾個方法都是為了 和傳統的ADO.NET集成的。
ExecuteCommand可以做insert,update,delete 操作,第一個參數接受一個格式化的sql語句,第二個參數是參數數組:
使用起來很簡單:
dbCtx.ExecuteCommand("insert into posts (blogid,title,body) values({0},{1},{2})", "2", "走 進Linq-How do I(4)", "廢話一篇");
ExecuteQuery<TResult>方法呢?顧名思義,就是執行查詢的, 也有兩個參數,第一個接受查詢的SQL語句,第二個接受參數,返回一個 IEnumerable<T>對象:
IEnumerable<Post> posts = dbCtx.ExecuteQuery<Post>("select * from posts where postid = {0}", "1");
foreach (var post in posts)
Console.WriteLine (post.Title);
Translate方法是將以前的DataReader轉換為現在 的Linq to SQL方式,這樣有什麼好處呢:
1.可以利用現在的映射,不再 需要這樣的語句post.Title = dr[1].ToString();
2.可以使用查詢表達 式的語法
對於插入,更新,刪除這些經常使用的操作,為了提高性能, 或者做一些自定義的操作,你還可以覆蓋微軟默認為你提供的Insert,Update等 方法:
1.寫一個存儲過程
CREATE PROCEDURE dbo.CreatePost
(
@blogid int,
@title nvarchar(50),
@body nvarchar(500)
)
AS
insert into posts (blogid,title,body) values(@blogid,@title,@body)
RETURN
當然,這個存儲過程非常簡單,不值得這麼去做,你就 假設存在一個復雜的存儲過程
2.把Posts表拖到設計器上,上面的存儲過 程拖到方法設計器上
3.右鍵點擊設計器上的表,在右鍵菜單裡會有一項 :配置行為(Config Behavior):
這樣就可以用你自定義的存儲過程做插入操作了。
Linq to SQL中的事務
事務這個東西在並發的時候特別重要,Linq to SQL本身就 支持事務,不過是樂觀鎖。我們也可以顯式的啟動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();
}
DbConnection的BeginTransaction方法還可以接受一個枚舉參數 IsolationLevel,來指定事務隔離的級別。
對於那些多個DataContext之 間的事務,我們可以使用.NET 2.0引入的TransactionScope
下一篇談談 RowVersion的問題