簡單學(xué)習(xí)Linq to sql存儲(chǔ)過程
在介紹Linq to sql存儲(chǔ)過程之前,先來介紹一下普通存儲(chǔ)過程,這樣更有助于大家對(duì)Linq to sql存儲(chǔ)過程的理解。
首先在查詢分析器運(yùn)行下面的代碼來創(chuàng)建一個(gè)存儲(chǔ)過程:
- create proc sp_singleresultset
- as
- set nocount on
- select * from customers
然后打開IDE的服務(wù)器資源管理器,之前我們從表中拖動(dòng)表到dbml設(shè)計(jì)視圖,這次我們從存儲(chǔ)過程中找到剛才創(chuàng)建的存儲(chǔ)過程,然后拖動(dòng)到設(shè)計(jì)視圖。在方法面板中可以看到已經(jīng)創(chuàng)建了一個(gè)sp_singleresultset的方法。
然后打開Northwind.designer.cs,可以找到下面的代碼:
- [Function(Name="dbo.sp_singleresultset")]
- public ISingleResult sp_singleresultset()
- {
- IExecuteResult result = this.ExecuteMethodCall(this,
- ((MethodInfo)(MethodInfo.GetCurrentMethod())));
- return ((ISingleResult)(result.ReturnValue));
- }
我們可以發(fā)現(xiàn),IDE為這個(gè)Linq to sql存儲(chǔ)過程單獨(dú)生成了返回結(jié)果集的實(shí)體定義,你可能會(huì)覺得很奇怪,IDE怎么知道這個(gè)存儲(chǔ)過程將會(huì)返回哪些數(shù)據(jù)那?其實(shí),在把存儲(chǔ)過程拖拽入dbml設(shè)計(jì)視圖的時(shí)候,IDE就執(zhí)行了類似下面的命令:
- SET FMTONLY ON;
- exec Northwind.dbo.sp_singleresultset
- SET FMTONLY OFF;
這樣就可以直接獲取Linq to sql存儲(chǔ)過程返回的元數(shù)據(jù)而無須執(zhí)行存儲(chǔ)過程。
其實(shí)我們存儲(chǔ)過程返回的就是顧客表的數(shù)據(jù),如果你覺得為存儲(chǔ)過程單獨(dú)設(shè)置結(jié)果集實(shí)體有些浪費(fèi)的話可以在存儲(chǔ)過程的屬性窗口中調(diào)整返回類型從“自動(dòng)生成的類型”到Customer,不過以后你只能通過刪除方法面板中的存儲(chǔ)過程,然后重新添加來還原到“自動(dòng)生成的類型”。下面,我們可以寫如下的Linq to object代碼進(jìn)行查詢:
var 單結(jié)果集存儲(chǔ)過程 =
- from c in ctx.sp_singleresultset()
- where c.CustomerID.StartsWith("A")
- select c;
在這里確實(shí)是Linq to object的,因?yàn)椴樵兙浞ú粫?huì)被整句翻譯成SQL,而是從Linq to sql存儲(chǔ)過程存儲(chǔ)過程的返回對(duì)象中再去對(duì)對(duì)象進(jìn)行查詢。SQL代碼如下:
- EXEC @RETURN_VALUE = [dbo].[sp_singleresultset]
- -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []
Linq to sql存儲(chǔ)過程之帶參數(shù)的存儲(chǔ)過程
創(chuàng)建如下存儲(chǔ)過程:
- create proc [dbo].[sp_withparameter]
- @customerid nchar(5),
- @rowcount int output
- as
- set nocount on
- set @rowcount = (select count(*) from customers where
- customerid = @customerid)
使用同樣的方法生成存儲(chǔ)過程方法,然后使用下面的代碼進(jìn)行測(cè)試:
- int? rowcount = -1;
- ctx.sp_withparameter("", ref rowcount);
- Response.Write(rowcount);
- ctx.sp_withparameter("ALFKI", ref rowcount);
- Response.Write(rowcount);
結(jié)果輸出了“01”。說明ID為“”的顧客數(shù)為0,而ID為“ALFKI”的顧客數(shù)為1。Linq to sql存儲(chǔ)過程存儲(chǔ)過程的輸出參數(shù)被封裝成了ref參數(shù),對(duì)于C#語法來說非常合情合理。SQL代碼如下
- EXEC @RETURN_VALUE = [dbo].[sp_withparameter] @customerid = @p0,
- @rowcount = @p1 OUTPUT
- -- @p0: Input StringFixedLength (Size = 5; Prec = 0; Scale = 0) []
- -- @p1: InputOutput Int32 (Size = 0; Prec = 0; Scale = 0) [-1]
- -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []
Linq to sql存儲(chǔ)過程之帶返回值的存儲(chǔ)過程
再來創(chuàng)建第三個(gè)存儲(chǔ)過程:
- create proc [dbo].[sp_withreturnvalue]
- @customerid nchar(5)
- as
- set nocount on
- if exists (select 1 from customers where customerid = @customerid)
- return 101
- else
- return 100
生成方法后,可以通過下面的代碼進(jìn)行測(cè)試:
- Response.Write(ctx.sp_withreturnvalue(""));
- Response.Write(ctx.sp_withreturnvalue("ALFKI"));
運(yùn)行后程序輸出“100101”
Linq to sql存儲(chǔ)過程之多結(jié)果集的存儲(chǔ)過程
再來創(chuàng)建一個(gè)多結(jié)果集的存儲(chǔ)過程:
- create proc [dbo].[sp_multiresultset]
- as
- set nocount on
- select * from customers
- select * from employees
找到生成的存儲(chǔ)過程方法:
- [Function(Name="dbo.sp_multiresultset")]
- public ISingleResult sp_multiresultset()
- {
- IExecuteResult result = this.ExecuteMethodCall(this,
- ((MethodInfo)(MethodInfo.GetCurrentMethod())));
- return ((ISingleResult)(result.ReturnValue));
- }
由于現(xiàn)在的VS2008會(huì)把多結(jié)果集Linq to sql存儲(chǔ)過程識(shí)別為單結(jié)果集存儲(chǔ)過程(只認(rèn)識(shí)第一個(gè)結(jié)果集),我們只能對(duì)Linq to sql存儲(chǔ)過程方法多小動(dòng)手術(shù),修改為:
- [Function(Name="dbo.sp_multiresultset")]
- [ResultType(typeof(Customer))]
- [ResultType(typeof(Employee))]
- public IMultipleResults sp_multiresultset()
- {
- IExecuteResult result = this.ExecuteMethodCall(this,
- ((MethodInfo)(MethodInfo.GetCurrentMethod())));
- return (IMultipleResults)(result.ReturnValue);
- }
然后使用下面的代碼測(cè)試:
- var 多結(jié)果集存儲(chǔ)過程 = ctx.sp_multiresultset();
- var Customers = 多結(jié)果集存儲(chǔ)過程.GetResult();
- var Employees = 多結(jié)果集存儲(chǔ)過程.GetResult();
- GridView1.DataSource = from emp in Employees where
- emp.FirstName.Contains("A") select emp;
- GridView1.DataBind();
- GridView2.DataSource = from c in Customers where
- c.CustomerID.StartsWith("A") select c;
- GridView2.DataBind();
使用Linq to sql存儲(chǔ)過程新增數(shù)據(jù)
存儲(chǔ)過程除了可以直接調(diào)用之外,還可以用于實(shí)體的增刪改操作。還記得在《一步一步學(xué)Linq to sql(三):增刪改》中創(chuàng)建的留言簿程序嗎?下面我們就來改造這個(gè)程序,使用存儲(chǔ)過程而不是系統(tǒng)生成的SQL實(shí)現(xiàn)實(shí)體增刪改。首先,我們創(chuàng)建下面的Linq to sql存儲(chǔ)過程。
- create proc sendmessage
- @username varchar(50),
- @message varchar(500)
- as
- insert into tbguestbook
- (id,username,posttime,[message],isreplied,reply)
- values
- (newid(),@username,getdate(),@message,0,'')
然后,打開留言簿dbml,把Linq to sql存儲(chǔ)過程從服務(wù)器資源管理器拖拽到設(shè)計(jì)視圖上。右鍵點(diǎn)擊tbGuestBook實(shí)體類,選擇配置行為。如下圖,為插入操作選擇剛才創(chuàng)建的存儲(chǔ)過程方法,并進(jìn)行參數(shù)匹配:
由于我們的Linq to sql存儲(chǔ)過程只接受2個(gè)參數(shù),相應(yīng)修改以下創(chuàng)建留言的按鈕處理事件:
- protected void btn_SendMessage_Click(object sender, EventArgs e)
- {
- tbGuestBook gb = new tbGuestBook();
- gb.UserName = tb_UserName.Text;
- gb.Message = tb_Message.Text;
- ctx.tbGuestBooks.Add(gb);
- ctx.SubmitChanges();
- SetBind();
- }
運(yùn)行程序后可以發(fā)現(xiàn),在提交修改的時(shí)候調(diào)用了下面的SQL:
- EXEC @RETURN_VALUE = [dbo].[sendmessage] @username = @p0, @message = @p1
- -- @p0: Input AnsiString (Size = 5; Prec = 0; Scale = 0) [zhuye]
- -- @p1: Input AnsiString (Size = 11; Prec = 0; Scale = 0) [new message]
- -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []
使用Linq to sql存儲(chǔ)過程刪除數(shù)據(jù)
創(chuàng)建如下Linq to sql存儲(chǔ)過程:
- create proc delmessage
- @id uniqueidentifier
- as
- delete tbguestbook where id=@id
按照前面的步驟生成Linq to sql存儲(chǔ)過程方法,并為刪除操作執(zhí)行這個(gè)存儲(chǔ)過程方法。在選擇參數(shù)的時(shí)候我們可以看到,ID分當(dāng)前值和原始值,我們選擇當(dāng)前值即可無須改動(dòng)任何邏輯代碼,進(jìn)行刪除留言操作后可以跟蹤到下面的SQL:
- EXEC @RETURN_VALUE = [dbo].[delmessage] @id = @p0
- -- @p0: Input Guid (Size = 0; Prec = 0; Scale = 0)
- [9e3c5ee3-2575-458e-899d-4b0bf73e0849]
- -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []
Linq to sql存儲(chǔ)過程之使用存儲(chǔ)過程更改數(shù)據(jù)
創(chuàng)建如下存儲(chǔ)過程:
- create proc replymessage
- @id uniqueidentifier,
- @reply varchar(500)
- as
- update tbguestbook set reply=@reply,isreplied=1 where id=@id
由于更新的時(shí)候并不會(huì)更新主鍵,所以我們可以為兩個(gè)參數(shù)都指定當(dāng)前值?;貜?fù)留言后可以跟蹤到下面的SQL:
- EXEC @RETURN_VALUE = [dbo].[replymessage] @id = @p0, @reply = @p1
- -- @p0: Input Guid (Size = 0; Prec = 0; Scale = 0)
- [67a69d0f-a88b-4b22-8939-fed021eb1cb5]
- -- @p1: Input AnsiString (Size = 6; Prec = 0; Scale = 0) [464456]
- -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []
假設(shè)有這樣一種應(yīng)用,我們需要修改留言簿中不合法的用戶名:
- create proc modiusername
- @oldusername varchar(50),
- @newusername varchar(50)
- as
- update tbguestbook set username=@newusername where
- username = @oldusername
有個(gè)網(wǎng)友起名叫“admin”,我們要把所有這個(gè)名字修改為“notadmin”。那么,可以如下圖設(shè)置update操作:
然后運(yùn)行下面的測(cè)試代碼:
- var messages = from gb in ctx.tbGuestBooks
- select gb;
- foreach (var gb in messages)
- {
- if(gb.UserName == "admin")
- gb.UserName = "notadmin";
- }
運(yùn)行程序后能跟蹤到下面的SQL:
- SELECT [t0].[ID], [t0].[UserName], [t0].[PostTime], [t0].[Message],
- [t0].[IsReplied], [t0].[Reply]
- FROM [dbo].[tbGuestBook] AS [t0]
- EXEC @RETURN_VALUE = [dbo].[modiusername] @oldusername =
- @p0, @newusername = @p1
- -- @p0: Input AnsiString (Size = 5; Prec = 0; Scale = 0) [admin]
- -- @p1: Input AnsiString (Size = 8; Prec = 0; Scale = 0) [notadmin]
- -- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []
Linq to sql存儲(chǔ)過程就看到這里了,你應(yīng)該能明白當(dāng)前值和原始值的含義了吧。
【編輯推薦】