zoukankan      html  css  js  c++  java
  • 一步一步学Linq to sql(五):存储过程 (非原创)

    普通存储过程

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

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

           到这里,你应该能明白当前值和原始值的含义了吧。

  • 相关阅读:
    Exchange 2013与 Office Web Apps 整合
    SharePoint2013 以其他用户登录和修改AD域用户密码 功能
    sharepoint 2010 自定义页面布局
    sharepoint 2010 记录管理 对象模型
    SharePoint2010 对象模型 关联列表
    在SharePoint Server 2010中更改“我的网站”
    xenapp 6.5 客户端插件第一次安装总是跳到官网
    如何解决在Windows Server 2008 R2 上安装证书服务重启后出现 CertificationAuthority 91错误事件
    在Win7 Hyper-v虚拟机中挂接真实机的声卡
    win8 中如何删除 共享文件夹 用户名和密码
  • 原文地址:https://www.cnblogs.com/wangjingblogs/p/2263520.html
Copyright © 2011-2022 走看看