普通存储过程

首先在查询分析器运行下面的代码来创建一个存储过程:

create proc sp_singleresultset

as

set nocount on

select * from customers

然后打开IDE的服务器资源管理器,之前我们从表中拖动表到dbml设计视图,这次我们从存储过程中找到刚才创建的存储过程,然后拖动到设计视图。在方法面板中可以看到已经创建了一个sp_singleresultset的方法,如下图:

然后打开Northwind.designer.cs,可以找到下面的代码:

[Function(Name="dbo.sp_singleresultset")]

public ISingleResult<sp_singleresultsetResult> sp_singleresultset()

{

IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())));

return ((ISingleResult<sp_singleresultsetResult>)(result.ReturnValue));

}

我们可以发现,IDE为这个存储过程单独生成了返回结果集的实体定义,你可能会觉得很奇怪,IDE怎么知道这个存储过程将会返回哪些数据那?其实,在把存储过程拖拽入dbml设计视图的时候,IDE就执行了类似下面的命令:

SET FMTONLY ON;

exec Northwind.dbo.sp_singleresultset

SET FMTONLY OFF;

这样就可以直接获取存储过程返回的元数据而无须执行存储过程。

其实我们存储过程返回的就是顾客表的数据,如果你觉得为存储过程单独设置结果集实体有些浪费的话可以在存储过程的属性窗口中调整返回类型从“自动生成的类型”到Customer,不过以后你只能通过删除方法面板中的存储过程,然后重新添加来还原到“自动生成的类型”。下面,我们可以写如下的Linq to object代码进行查询:

var 单结果集存储过程 =

from c in ctx.sp_singleresultset()

where c.CustomerID.StartsWith("A")

select c;

在这里确实是Linq to object的,因为查询句法不会被整句翻译成SQL,而是从存储过程的返回对象中再去对对象进行查询。SQL代码如下:

EXEC @RETURN_VALUE = [dbo].[sp_singleresultset]

-- @RETURN_VALUE: Output Int32 (Size = 0; Prec = 0; Scale = 0) []

带参数的存储过程

创建如下存储过程:

create proc [dbo].[sp_withparameter]

@customerid nchar(5),

@rowcount int output

as

set nocount on

set @rowcount = (select count(*) from customers where customerid = @customerid)

使用同样的方法生成存储过程方法,然后使用下面的代码进行测试:

int? rowcount = -1;

ctx.sp_withparameter("", ref rowcount);

Response.Write(rowcount);

ctx.sp_withparameter("ALFKI", ref rowcount);

Response.Write(rowcount);

结果输出了“01”。说明ID为“”的顾客数为0,而ID为“ALFKI”的顾客数为1。存储过程的输出参数被封装成了ref参数,对于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) []

带返回值的存储过程

再来创建第三个存储过程:

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

生成方法后,可以通过下面的代码进行测试:

Response.Write(ctx.sp_withreturnvalue(""));

Response.Write(ctx.sp_withreturnvalue("ALFKI"));

运行后程序输出“100101”

 

多结果集的存储过程

再来创建一个多结果集的存储过程:

create proc [dbo].[sp_multiresultset]

as

set nocount on

select * from customers

select * from employees

找到生成的存储过程方法:

[Function(Name="dbo.sp_multiresultset")]

public ISingleResult<sp_multiresultsetResult> sp_multiresultset()

{

IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())));

return ((ISingleResult<sp_multiresultsetResult>)(result.ReturnValue));

}

由于现在的VS2008会把多结果集存储过程识别为单结果集存储过程(只认识第一个结果集),我们只能对存储过程方法多小动手术,修改为:

[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);

}

然后使用下面的代码测试:

var 多结果集存储过程 = ctx.sp_multiresultset();

var Customers = 多结果集存储过程.GetResult<Customer>();

var Employees = 多结果集存储过程.GetResult<Employee>();

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(三):增删改》中创建的留言簿程序吗?下面我们就来改造这个程序,使用存储过程而不是系统生成的SQL实现实体增删改。首先,我们创建下面的存储过程

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,把存储过程从服务器资源管理器拖拽到设计视图上。右键点击tbGuestBook实体类,选择配置行为。如下图,为插入操作选择刚才创建的存储过程方法,并进行参数匹配:

由于我们的存储过程只接受2个参数,相应修改以下创建留言的按钮处理事件:

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();

}

运行程序后可以发现,在提交修改的时候调用了下面的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) []

 

使用存储过程删除数据

创建如下存储过程:

create proc delmessage

@id uniqueidentifier

as

delete tbguestbook where id=@id

按照前面的步骤生成存储过程方法,并为删除操作执行这个存储过程方法。在选择参数的时候我们可以看到,ID分当前值和原始值,我们选择当前值即可,如下图:

无须改动任何逻辑代码,进行删除留言操作后可以跟踪到下面的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) []

 

使用存储过程更改数据

创建如下存储过程:

create proc replymessage

@id uniqueidentifier,

@reply varchar(500)

as

update tbguestbook set reply=@reply,isreplied=1 where id=@id

       由于更新的时候并不会更新主键,所以我们可以为两个参数都指定当前值。回复留言后可以跟踪到下面的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) []

假设有这样一种应用,我们需要修改留言簿中不合法的用户名:

create proc modiusername

@oldusername varchar(50),

@newusername varchar(50)

as

update tbguestbook set username=@newusername where username = @oldusername

有个网友起名叫“admin”,我们要把所有这个名字修改为“notadmin”。那么,可以如下图设置update操作:

然后运行下面的测试代码:

var messages = from gb in ctx.tbGuestBooks

select gb;

foreach (var gb in messages)

{

if(gb.UserName == "admin")

gb.UserName = "notadmin";

}

运行程序后能跟踪到下面的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(五):存储过程的更多相关文章

  1. (转载)一步一步学Linq to sql系列文章

    现在Linq to sql的资料还不是很多,本人水平有限,如果有错或者误导请指出,谢谢. 一步一步学Linq to sql(一):预备知识 一步一步学Linq to sql(二):DataContex ...

  2. 步步学LINQ to SQL:为实体类添加关系【转】

    [IT168 专稿]本文详细为你阐述了如何在你的应用程序中实现LINQ to SQL.附件的示例程序包括了这里探讨的所有代码,还提供了一个简单的WPF图形界面程序来显示通过数据绑定返回的结果集. 第一 ...

  3. 步步学LINQ to SQL:使用LINQ检索数据【转】

    [IT168 专稿]该系列教程描述了如何采用手动的方式映射你的对象类到数据表(而不是使用象SqlMetal这样的自动化工具)以便能够支持数据表之间的M:M关系和使用实体类的数据绑定.即使你选择使用了自 ...

  4. 步步学LINQ to SQL:将类映射到数据库表【转】

    [IT168 专稿]该系列教程描述了如何采用手动的方式映射你的对象类到数据表(而不是使用象SqlMetal这样的自动化工具)以便能够支持数据表之间的M:M关系和使用实体类的数据绑定.即使你选择使用了自 ...

  5. 一步一步学Linq to sql(六):探究特性

    延迟执行 IQueryable query = from c in ctx.Customers select c; 这样的查询句法不会导致语句立即执行,它仅仅是一个描述,对应一个SQL.仅仅在需要使用 ...

  6. 一步一步学Linq to sql(四):查询句法

    select 描述:查询顾客的公司名.地址信息 查询句法: var 构建匿名类型1 = from c in ctx.Customers select new { 公司名 = c.CompanyName ...

  7. 一步一步学Linq to sql(三):增删改

    示例数据库 字段名 字段类型 允许空 字段说明 ID uniqueidentifier 表主键字段 UserName varchar(50) 留言用户名 PostTime datetime 留言时间 ...

  8. 一步一步学Linq to sql(二):DataContext与实体

    DataContext DataContext类型(数据上下文)是System.Data.Linq命名空间下的重要类型,用于把查询句法翻译成SQL语句,以及把数据从数据库返回给调用方和把实体的修改写入 ...

  9. 一步一步学Linq to sql(一):预备知识

    什么是Linq to sql Linq to sql(或者叫DLINQ)是LINQ(.NET语言集成查询)的一部分,全称基于关系数据的 .NET 语言集成查询,用于以对象形式管理关系数据,并提供了丰富 ...

随机推荐

  1. C# 调用 SQL server 初探

    相信不少人都和我一样: 1.学过数据库原理接触过SQL Server,做过一套卷子外加一个数据库设计作业: 2.学过C# 但从来还没在程序里用到过数据库(哈哈,新手躺枪) 这也是我第一次在C#里用数据 ...

  2. 使用Gardener在Google Cloud Platform上创建Kubernetes集群

    Gardener是一个开源项目,github地址: https://github.com/gardener/gardener/ 使用Gardener,我们可以在几分钟之内在GCP, AWS, Azur ...

  3. 【转载】#458 Errors While Converting Between enum and Underlying Type

    You can convert to an enum value from its underlying type by casting the underlying type (e.g. int) ...

  4. python 用cookie模拟登陆网站

    import re import requests def get_info(url): headers = { "Cookie" :"***************** ...

  5. 塔防cocos2d

    塔防游戏,类似于保卫萝卜的一种. 需要注意的是几点问题是: 游戏地图是瓦片地图,设置特定的标记,用来标记哪些点是地图点,哪些是塔点. 游戏关卡选择:需要在两个cpp文件传参,用的是静态成员变量. 每一 ...

  6. Sublime Text 插件 【转】

    好厉害,好漂亮. http://www.cnsecer.com/460.html 安装Sublime Text 3插件的方法: 朋友们,小站活着不容易,全靠广告费养着了,如果本文对你有帮助.麻烦动下手 ...

  7. echarts图表与可视窗口的自适应

    由于要适应屏幕尺寸,发现了这个问题.网上搜到了两个办法,如下: 方法一: window.onresize = mychart.resize; 方法二: window.addEventListener( ...

  8. require,import区别?

    遵循的模块化规范不一样 模块化规范:即为 JavaScript 提供一种模块编写.模块依赖和模块运行的方案.谁让最初的 JavaScript 是那么的裸奔呢——全局变量就是它的模块化规范. requi ...

  9. axios简单了解

    简单介绍 axios是基于客户端的promise,面向浏览器和nodejs 特色 浏览器端发起XMLHttpRequests请求 node端发起http请求 支持Promise API 监听请求和返回 ...

  10. wordpress建设的企业网站

    wordpress企业站 http://ahlajd.demo.phpfangzhan.com 后台焦点图实现: