精华内容
下载资源
问答
  • 摘要:在一个项目的开发中,经常会...本文在利用数据库提供的系统表原理上,实现了统一调用方法,该方法只需要提供要调用存储过程名,以及调用时提供具体的参数值就可实现任何存储过程调用。 Abstract: ...

    摘要:在一个项目的开发中,经常会调用数据库中的存储过程。可是,几乎所有存储过程的调用都是同一个模式,主要区别就在于创建的每个参数类型、值等不一样。那么,能不能实现通过一个函数(或者类)调用所有的存储过程呢?本文在利用数据库提供的系统表原理上,实现了统一调用的方法,该方法只需要提供要调用的存储过程名,以及调用时提供具体的参数值就可实现任何存储过程的调用。

     

     

    Abstract: We have to call stored procedures of database systems during a development of a project. However, calling a stored procedures are almost the same, the main difference is the difference between parameters’ type or value etc. Can we call any stored procedures through a function (or a class)? Based on the system tables provided by database systems, We wrote a class to call any stored procedures in this article. To call a stored procedure, the only parameters you provide are the name of the stored procedure and the value of all parameters of the stored procedure.

    1.       引言

    在各种系统开发中,使用存储过程是一个良好的习惯,不仅可以带来临时表、函数、游标等特性,而且调试、升级、维护都变得方便。在存储过程中能够把数据经过处理再返回,这样能够对数据提供更多的分析和控制。

    在存储过程的调用中,我们发现存储过程的调用都几乎是如下的模式:

    1.声明SqlConnection

    2.声明SqlCommand,并且设置其Connection属性为刚声明的SqlConnection实例,设置CommandName为存储过程名,CommandType为存储过程。

    3.往刚声明的SqlCommand实例的Parameters集合中添加所有的存储过程调用需要的参数

     

    4.呼叫SqlCommand的ExecuteReader()方法来得到存储过程的返回行集

     

    4.声明SqlDataAdapter和DataSet,设置SqlDataAdapter的SelectCommand属性为3中声明的实例,再调用其Fill方法来把返回的行集填充到DataSet中

     

    5.关闭SqlConnection对象

    6.释放声明的各对象实例

    (说明:4指的是两种数据提取方法)

    在这个调用过程中,我们发现几乎所有的存储过程调用都是这个模式,之间的区别就在第2步中的存储过程名不同和第3步中各个存储过程调用使用的参数是不一样的,他们有参数名字、方向、数据类型、长度等的区别。

    那么,有没有一种方法可以实现所有的存储过程调用?即只需要提供存储过程名,然后把参数值传入调用方法即可实现存储过程的调用,再用某些数据结构来保存返回的行集、传出参数值、过程返回值。经过研究SQL Server的系统表,我们发现这个想法是切实可行的。

    2.系统表与信息结构视图

           SQL Server等关系型数据库都将元数据以某种方式保存在数据库中,在SQL Server中就是系统数据库和系统表。安装SQL Server后会自动生成四个系统数据库:master, model, msdb与tempdb。master数据库是SQL Server中所有系统级信息的仓库。登录帐号、配置设置、系统存储过程和其他数据库的存在性都记录在master数据库中。msdb数据库保存SQL Server Agent的信息。定义作业、操作员和警报时,他们存放在msdb中。model是个模框,用于所有用户生成的数据库。生成新数据库时,将model复制,建立所要的对象。tempdb保存SQL Server中的临时对象。显示生成的临时表和临时存储过程以及系统生成的临时对象都利用tempdb。[1]

           而且每个数据库中都有自己的系统表。这些系统表被用来保存配置和对象信息。从这些系统表中,我们就可以得到每个存储过程的所有参数的信息。syscolumns表中就保存了这些信息。其中有参数名、类型、长度、方向等需要用到我们方法中的信息。

           不过,系统表中的字段会随着SQL Server版本的变化而变化。比如syscolumns中的type和xtype就是这样的一个变化例子,他们都保存了类型的信息。要让我们的方法适应SQL Server的版本变化要求,就要用到信息结构视图。

           ANSI-92将信息结构视图定义为一组提供系统数据的视图。通过利用该视图,可以将实际系统表从应用程序中隐藏起来。系统表的改变就不会影响到应用程序,这样应用程序就可以独立于数据库厂家和版本。[1]

           ANSI-92和SQL Server支持用三段命名结构引用本地服务器上的对象。ANSI-92术语称为catalog.schema.object,而SQL Server称为database.owner.object。[1]比如我们要找到所有存储过程的所有参数信息,就可以用:

           select * from INFORMATION_SCHEMA.PARAMETERS

    如果要找到某个存储过程的所有参数信息,就是:

           select * from INFORMATION_SCHEMA.PARAMETERS where SPECIFIC_NAME =’Proc1’

    有了信息结构视图,我们的问题就解决了一大半了。下面我们看如何在.NET中实现我们的方法。

     

    3.实现方法

    实现的重点就放在如何根据存储过程名来得到它的所有的参数信息,再根据这些参数信息自动的创建各个参数。为了让这些动作自动化,声明SqlConnection、SqlCommand、SqlParameter的过程,创建各个SqlParameter的过程对用户来说都应该不可见。用户唯一需要提供的就是存储过程的名字,然后就是在调用的时候提供各个参数,甚至连他们的类型都不需要提供。

    31获得和创建存储过程的参数

    如何获得并且创建要调用的存储过程的参数是一个重点,通过信息结构视图我们可以自动的实现这个步骤。

    // 获得和创建存储过程的参数
    private void GetProcedureParameter(params object[] parameters)
    {
            SqlCommand myCommand2 = new SqlCommand();
    
    
            myCommand2.Connection = this.myConnection;
            myCommand2.CommandText = "select * from INFORMATION_SCHEMA.PARAMETERS where SPECIFIC_NAME='" +this.ProcedureName+ "' order by ORDINAL_POSITION";
    
    
            SqlDataReader reader = null;
            reader = myCommand2.ExecuteReader();
    
    
                    // 创建返回参数
                    myParameter = new SqlParameter();
                    myParameter.ParameterName = "@Value";
                    myParameter.SqlDbType = SqlDbType.Int;
                    myParameter.Direction = ParameterDirection.ReturnValue;
    
    
                    myCommand.Parameters.Add(myParameter);
    
    
                    int i = 0;
                    // 创建各个参数,在这个地方可以自动的创建SqlParameter的类型,值,方向等属性
                    while(reader.Read())
                    {
                        myParameter = new SqlParameter();
    
    
                        myParameter.ParameterName = reader["PARAMETER_NAME"].ToString();
                        myParameter.Direction = reader["PARAMETER_MODE"].ToString()=="IN"?ParameterDirection.Input:ParameterDirection.Output;
    
    
                        switch(reader["DATA_TYPE"].ToString())
                        {
                                case "int" :
                                    if(myParameter.Direction == ParameterDirection.Input)
                                            myParameter.Value = (int)parameters;
                                    myParameter.SqlDbType = SqlDbType.Int;
    
    
                                    break;
                               //...省略了很多具体的类型处理
                                    default : break;
                        }
                        i++;
    
    
                        myCommand.Parameters.Add(myParameter);
                    }
    
    
    }

     

     

     

     

    32返回结果数据集、返回值、传出参数集

    创建好存储过程的参数之后,我们就可以调用这个存储过程了。由于在.NET中,常用的返回结果集的类为SqlDataReader和DataSet,而SqlDataReader必须在保持连接的状态下才可以使用,DataSet却不需要。在我们的实现中,连接应该在调用之后就断开,因此采用DataSet来保存返回结果集。

    public SqlResult Call(params object[] parameters)
    {
            // SqlResult是自己定义的用于保存结果数据集、返回值、传出参数集的类
            SqlResult result = new SqlResult();
    
    
            // 根据需要定义自己的连接字符串
            myConnection  = new SqlConnection(ConnectionString);
    
    
            myCommand = new SqlCommand(this.ProcedureName, myConnection);
            myCommand.CommandType = CommandType.StoredProcedure;
    
    
            SqlDataAdapter myAdapter = new SqlDataAdapter(myCommand);
    
    
            myConnection.Open();
            // 获得和创建存储过程的参数,并且设置好值
            GetProcedureParameter(parameters);
    
    
            myAdapter.Fill(result.dataSet, "Table");
    
    
            // 获得存储过程的传出参数值和名字对,保存在一个Hashtable中
            GetOutputValue(result);
    
    
            // 在这里释放各种资源,断开连接
            myAdapter.Dispose();
            myCommand.Dispose();
            myConnection.Close();
            myConnection.Dispose();
    
    
    
    
            return result;
    }

    4.进一步工作

           虽然我们在这里的实现是针对SQL Server数据库,但是对于任何提供了信息结构视图,符合ANSI-92标准,或者是提供了元数据的数据库都可以使用这种方法来实现。我们把它封装成一个SqlProcedure类,在需要的时候可以很简单的就调用了存储过程,减少了大量基本上是重复的代码工作。

           为了让SqlProcedure类支持更过的数据类型,在GetProcedureParameter()方法中需要根据自己的需要来分析各个参数的类型、方向、长度、默认值等信息,然后来创建这个参数。基本上任何类型都是能够实现的,甚至连image类型都可以采用这种方式创建。这样这个类就可以很通用,在任何项目中都可以发挥作用

    转载于:https://www.cnblogs.com/Ruiky/archive/2012/03/26/2418173.html

    展开全文
  • .NET中统一的存储过程调用方法 摘要:在一个项目的开发中,经常会调用数据库中的存储过程。可是,几乎所有存储过程的调用都是同一个模式,主要区别就在于创建的每个参数类型、值等不一样。那么,能不能实现通过一个...
     
    
    .NET中统一的存储过程调用方法

    摘要:在一个项目的开发中,经常会调用数据库中的存储过程。可是,几乎所有存储过程的调用都是同一个模式,主要区别就在于创建的每个参数类型、值等不一样。那么,能不能实现通过一个函数(或者类)调用所有的存储过程呢?本文在利用数据库提供的系统表原理上,实现了统一调用的方法,该方法只需要提供要调用的存储过程名,以及调用时提供具体的参数值就可实现任何存储过程的调用。

     
    Abstract: We have to call stored procedures of database systems during a development of a project. However, calling a stored procedures are almost the same, the main difference is the difference between parameters’ type or value etc. Can we call any stored procedures through a function (or a class)? Based on the system tables provided by database systems, We wrote a class to call any stored procedures in this article. To call a stored procedure, the only parameters you provide are the name of the stored procedure and the value of all parameters of the stored procedure.
    1.       引言
    在各种系统开发中,使用存储过程是一个良好的习惯,不仅可以带来临时表、函数、游标等特性,而且调试、升级、维护都变得方便。在存储过程中能够把数据经过处理再返回,这样能够对数据提供更多的分析和控制。
    在存储过程的调用中,我们发现存储过程的调用都几乎是如下的模式:
    1.声明SqlConnection
    2.声明SqlCommand,并且设置其Connection属性为刚声明的SqlConnection实例,设置CommandName为存储过程名,CommandType为存储过程。
    3.往刚声明的SqlCommand实例的Parameters集合中添加所有的存储过程调用需要的参数
     
    4.呼叫SqlCommand的ExecuteReader()方法来得到存储过程的返回行集
     
    4.声明SqlDataAdapter和DataSet,设置SqlDataAdapter的SelectCommand属性为3中声明的实例,再调用其Fill方法来把返回的行集填充到DataSet中
     
    5.关闭SqlConnection对象
    6.释放声明的各对象实例
    (说明:4指的是两种数据提取方法)
    在这个调用过程中,我们发现几乎所有的存储过程调用都是这个模式,之间的区别就在第2步中的存储过程名不同和第3步中各个存储过程调用使用的参数是不一样的,他们有参数名字、方向、数据类型、长度等的区别。
    那么,有没有一种方法可以实现所有的存储过程调用?即只需要提供存储过程名,然后把参数值传入调用方法即可实现存储过程的调用,再用某些数据结构来保存返回的行集、传出参数值、过程返回值。经过研究SQL Server的系统表,我们发现这个想法是切实可行的。
    2.系统表与信息结构视图
           SQL Server等关系型数据库都将元数据以某种方式保存在数据库中,在SQL Server中就是系统数据库和系统表。安装SQL Server后会自动生成四个系统数据库:master, model, msdb与tempdb。master数据库是SQL Server中所有系统级信息的仓库。登录帐号、配置设置、系统存储过程和其他数据库的存在性都记录在master数据库中。msdb数据库保存SQL Server Agent的信息。定义作业、操作员和警报时,他们存放在msdb中。model是个模框,用于所有用户生成的数据库。生成新数据库时,将model复制,建立所要的对象。tempdb保存SQL Server中的临时对象。显示生成的临时表和临时存储过程以及系统生成的临时对象都利用tempdb。[1]
           而且每个数据库中都有自己的系统表。这些系统表被用来保存配置和对象信息。从这些系统表中,我们就可以得到每个存储过程的所有参数的信息。syscolumns表中就保存了这些信息。其中有参数名、类型、长度、方向等需要用到我们方法中的信息。
           不过,系统表中的字段会随着SQL Server版本的变化而变化。比如syscolumns中的type和xtype就是这样的一个变化例子,他们都保存了类型的信息。要让我们的方法适应SQL Server的版本变化要求,就要用到信息结构视图。
           ANSI-92将信息结构视图定义为一组提供系统数据的视图。通过利用该视图,可以将实际系统表从应用程序中隐藏起来。系统表的改变就不会影响到应用程序,这样应用程序就可以独立于数据库厂家和版本。[1]
           ANSI-92和SQL Server支持用三段命名结构引用本地服务器上的对象。ANSI-92术语称为catalog.schema.object,而SQL Server称为database.owner.object。[1]比如我们要找到所有存储过程的所有参数信息,就可以用:
           select * from INFORMATION_SCHEMA.PARAMETERS
    如果要找到某个存储过程的所有参数信息,就是:
           select * from INFORMATION_SCHEMA.PARAMETERS where SPECIFIC_NAME =’Proc1’
    有了信息结构视图,我们的问题就解决了一大半了。下面我们看如何在.NET中实现我们的方法。
     
    3.实现方法
    实现的重点就放在如何根据存储过程名来得到它的所有的参数信息,再根据这些参数信息自动的创建各个参数。为了让这些动作自动化,声明SqlConnection、SqlCommand、SqlParameter的过程,创建各个SqlParameter的过程对用户来说都应该不可见。用户唯一需要提供的就是存储过程的名字,然后就是在调用的时候提供各个参数,甚至连他们的类型都不需要提供。
    31获得和创建存储过程的参数
    如何获得并且创建要调用的存储过程的参数是一个重点,通过信息结构视图我们可以自动的实现这个步骤。
    // 获得和创建存储过程的参数
    private void GetProcedureParameter(params object[] parameters)
    {
            SqlCommand myCommand2 = new SqlCommand();
     
            myCommand2.Connection = this.myConnection;
            myCommand2.CommandText = "select * from INFORMATION_SCHEMA.PARAMETERS where SPECIFIC_NAME='" +this.ProcedureName+ "' order by ORDINAL_POSITION";
     
            SqlDataReader reader = null;
            reader = myCommand2.ExecuteReader();
     
                    // 创建返回参数
                    myParameter = new SqlParameter();
                    myParameter.ParameterName = "@Value";
                    myParameter.SqlDbType = SqlDbType.Int;
                    myParameter.Direction = ParameterDirection.ReturnValue;
     
                    myCommand.Parameters.Add(myParameter);
     
                    int i = 0;
                    // 创建各个参数,在这个地方可以自动的创建SqlParameter的类型,值,方向等属性
                    while(reader.Read())
                    {
                        myParameter = new SqlParameter();
     
                        myParameter.ParameterName = reader["PARAMETER_NAME"].ToString();
                        myParameter.Direction = reader["PARAMETER_MODE"].ToString()=="IN"?ParameterDirection.Input:ParameterDirection.Output;
     
                        switch(reader["DATA_TYPE"].ToString())
                        {
                                case "int" :
                                    if(myParameter.Direction == ParameterDirection.Input)
                                            myParameter.Value = (int)parameters[i];
                                    myParameter.SqlDbType = SqlDbType.Int;
                
                                    break;
                               //...省略了很多具体的类型处理
                                    default : break;
                        }
                        i++;
     
                        myCommand.Parameters.Add(myParameter);
                    }
     
    }
     
     
     
     
    32返回结果数据集、返回值、传出参数集
    创建好存储过程的参数之后,我们就可以调用这个存储过程了。由于在.NET中,常用的返回结果集的类为SqlDataReader和DataSet,而SqlDataReader必须在保持连接的状态下才可以使用,DataSet却不需要。在我们的实现中,连接应该在调用之后就断开,因此采用DataSet来保存返回结果集。
    public SqlResult Call(params object[] parameters)
    {
            // SqlResult是自己定义的用于保存结果数据集、返回值、传出参数集的类
            SqlResult result = new SqlResult();
     
            // 根据需要定义自己的连接字符串
            myConnection = new SqlConnection(ConnectionString);
     
            myCommand = new SqlCommand(this.ProcedureName, myConnection);
            myCommand.CommandType = CommandType.StoredProcedure;
     
            SqlDataAdapter myAdapter = new SqlDataAdapter(myCommand);
     
            myConnection.Open();
            // 获得和创建存储过程的参数,并且设置好值
            GetProcedureParameter(parameters);
     
            myAdapter.Fill(result.dataSet, "Table");
            
            // 获得存储过程的传出参数值和名字对,保存在一个Hashtable中
            GetOutputValue(result);
     
            // 在这里释放各种资源,断开连接
            myAdapter.Dispose();
            myCommand.Dispose();
            myConnection.Close();
            myConnection.Dispose();
     
     
            return result;
    }
    4.进一步工作
           虽然我们在这里的实现是针对SQL Server数据库,但是对于任何提供了信息结构视图,符合ANSI-92标准,或者是提供了元数据的数据库都可以使用这种方法来实现。我们把它封装成一个SqlProcedure类,在需要的时候可以很简单的就调用了存储过程,减少了大量基本上是重复的代码工作。
           为了让SqlProcedure类支持更过的数据类型,在GetProcedureParameter()方法中需要根据自己的需要来分析各个参数的类型、方向、长度、默认值等信息,然后来创建这个参数。基本上任何类型都是能够实现的,甚至连image类型都可以采用这种方式创建。这样这个类就可以很通用,在任何项目中都可以发挥作用。
    展开全文
  • 两种不同的存储过程调用方法 为了突出新方法的优点,首先介绍一下在.net中调用存储过程的“官方”方法。另外,本文的所有示例程序均工作于sqlserver数据库上,其它情况类似,以后不再一一说明。本文所有例子均采用...
    两种不同的存储过程调用方法

      为了突出新方法的优点,首先介绍一下在.net中调用存储过程的“官方”方法。另外,本文的所有示例程序均工作于sqlserver数据库上,其它情况类似,以后不再一一说明。本文所有例子均采用c#语言。

      要在应用程序中访问数据库,一般性的步骤是:首先声明一个数据库连接sqlconnection,然后声明一个数据库命令sqlcommand,用来执行sql语句和存储过程。有了这两个对象后,就可以根据自己的需要采用不同的执行方式达到目的。需要补充的是,不要忘记在页面上添加如下的引用语句:using system.data.sqlclient。

      就执行存储过程来说,如果执行的是第一类存储过程,那么就要用一个dataadapter将结果填充到一个dataset中,然后就可以使用数据网格控件将结果呈现在页面上了;如果执行的是第二和第三种存储过程,则不需要此过程,只需要根据特定的返回判定操作是否成功完成即可。

      (1)执行一个没有参数的存储过程的代码如下:

    程序代码 程序代码
    sqlconnection conn=new sqlconnection(“connectionstring”);
    sqldataadapter da = new sqldataadapter();
    da.selectcommand = new sqlcommand();
    da.selectcommand.connection = conn;
    da.selectcommand.commandtext = "nameofprocedure";
    da.selectcommand.commandtype = commandtype.storedprocedure;


      然后只要选择适当的方式执行此处过程,用于不同的目的即可。

      (2)执行一个有参数的存储过程的代码如下(我们可以将调用存储过程的函数声明为exeprocedure(string inputdate)):

    程序代码 程序代码
    sqlconnection conn=new sqlconnection(“connectionstring”);
    sqldataadapter da = new sqldataadapter();
    da.selectcommand = new sqlcommand();
    da.selectcommand.connection = conn;
    da.selectcommand.commandtext = "nameofprocedure";
    da.selectcommand.commandtype = commandtype.storedprocedure;
    (以上代码相同,以下为要添加的代码)
    param = new sqlparameter("@parametername", sqldbtype.datetime);
    param.direction = parameterdirection.input;
    param.value = convert.todatetime(inputdate);
    da.selectcommand.parameters.add(param);



      这样就添加了一个输入参数。若需要添加输出参数:

    程序代码 程序代码
    param = new sqlparameter("@parametername", sqldbtype.datetime);
    param.direction = parameterdirection.output;
    param.value = convert.todatetime(inputdate);
    da.selectcommand.parameters.add(param);



      若要获得参储过程的返回值:

    程序代码 程序代码
    param = new sqlparameter("@parametername", sqldbtype.datetime);
    param.direction = parameterdirection.returnvalue;
    param.value = convert.todatetime(inputdate);
    da.selectcommand.parameters.add(param);


      从上面的代码我们可以看出,当存储过程比较多或者存储过程的参数比较多时,这种方法会大大影响开发的速度;另外一方面,如果项目比较大,那么这些用于数据库逻辑的函数在以后的维护中也是一个很大的负担。那么,有没有一种改进的方法可以解决这个问题呢?想到在执行没有参数的存储过程时只需要传入一个存储过程的名字就可以调用相应的存储过程,而且在sqlserver数据库中我们可以直接在查询分析器中敲入“存储过程名(参数列表)”样的字符串就可以执行存储过程,那么,是否可以把这种思想应用到应用程序中呢? 

      于是在编译器中键入相应代码。这些代码是在调用不带参数的存储过程的代码的基础上改的。具体代码如下:

    程序代码 程序代码
    sqlconnection conn=new sqlconnection(“connectionstring”);
    sqldataadapter da = new sqldataadapter();
    da.selectcommand = new sqlcommand();
    da.selectcommand.connection = conn;
    da.selectcommand.commandtext = "nameofprocedure(’para1’,’para2’,para3)";
    da.selectcommand.commandtype = commandtype.storedprocedure;


      为了使代码更具有代表性,要调用的存储过程的第一个和第二个参数都为字符串类型,第三个参数为整型。执行以后发现,完全可以达到预期的效果! 

      两种调用方法的比较
      
      通过比较我们可以看到,第二种方法具有一个很明显的优点,那就是可以提高开发速度,节省开发时间,而且代码容易维护,在一定程度上也减少了系统大小。但是,由于对存储过程参数的处理比较笼统,如果要获取输出参数或者得到存储过程的返回值,这种方法就不能满足需要了。虽然如此,但是,这种方法毕竟可以让开发人员少些很大一部分的代码。如果不需要获取输出参数和返回值,那么几乎可以做到“一劳永逸”。因此在实际的程序开发中,这种方法还是具有一定的实用价值的。
     
    展开全文
  • .NET中统一的存储过程调用方法(收藏) 摘要:在一个项目的开发中,经常会调用数据库中的存储过程。可是,几乎所有存储过程的调用都是同一个模式,主要区别就在于创建的每个参数类型、值等不一样。那么,能不能实现...
    
    

    摘要:在一个项目的开发中,经常会调用数据库中的存储过程。可是,几乎所有存储过程的调用都是同一个模式,主要区别就在于创建的每个参数类型、值等不一样。那么,能不能实现通过一个函数(或者类)调用所有的存储过程呢?本文在利用数据库提供的系统表原理上,实现了统一调用的方法,该方法只需要提供要调用的存储过程名,以及调用时提供具体的参数值就可实现任何存储过程的调用。

     
    Abstract: We have to call stored procedures of database systems during a development of a project. However, calling a stored procedures are almost the same, the main difference is the difference between parameters’ type or value etc. Can we call any stored procedures through a function (or a class)? Based on the system tables provided by database systems, We wrote a class to call any stored procedures in this article. To call a stored procedure, the only parameters you provide are the name of the stored procedure and the value of all parameters of the stored procedure.
    1.       引言
    在各种系统开发中,使用存储过程是一个良好的习惯,不仅可以带来临时表、函数、游标等特性,而且调试、升级、维护都变得方便。在存储过程中能够把数据经过处理再返回,这样能够对数据提供更多的分析和控制。
    在存储过程的调用中,我们发现存储过程的调用都几乎是如下的模式:
    1.声明SqlConnection
    2.声明SqlCommand,并且设置其Connection属性为刚声明的SqlConnection实例,设置CommandName为存储过程名,CommandType为存储过程。
    3.往刚声明的SqlCommand实例的Parameters集合中添加所有的存储过程调用需要的参数
     
    4.呼叫SqlCommand的ExecuteReader()方法来得到存储过程的返回行集
     
    4.声明SqlDataAdapter和DataSet,设置SqlDataAdapter的SelectCommand属性为3中声明的实例,再调用其Fill方法来把返回的行集填充到DataSet中
     
    5.关闭SqlConnection对象
    6.释放声明的各对象实例
    (说明:4指的是两种数据提取方法)
    在这个调用过程中,我们发现几乎所有的存储过程调用都是这个模式,之间的区别就在第2步中的存储过程名不同和第3步中各个存储过程调用使用的参数是不一样的,他们有参数名字、方向、数据类型、长度等的区别。
    那么,有没有一种方法可以实现所有的存储过程调用?即只需要提供存储过程名,然后把参数值传入调用方法即可实现存储过程的调用,再用某些数据结构来保存返回的行集、传出参数值、过程返回值。经过研究SQL Server的系统表,我们发现这个想法是切实可行的。
    2.系统表与信息结构视图
           SQL Server等关系型数据库都将元数据以某种方式保存在数据库中,在SQL Server中就是系统数据库和系统表。安装SQL Server后会自动生成四个系统数据库:master, model, msdb与tempdb。master数据库是SQL Server中所有系统级信息的仓库。登录帐号、配置设置、系统存储过程和其他数据库的存在性都记录在master数据库中。msdb数据库保存SQL Server Agent的信息。定义作业、操作员和警报时,他们存放在msdb中。model是个模框,用于所有用户生成的数据库。生成新数据库时,将model复制,建立所要的对象。tempdb保存SQL Server中的临时对象。显示生成的临时表和临时存储过程以及系统生成的临时对象都利用tempdb。[1]
           而且每个数据库中都有自己的系统表。这些系统表被用来保存配置和对象信息。从这些系统表中,我们就可以得到每个存储过程的所有参数的信息。syscolumns表中就保存了这些信息。其中有参数名、类型、长度、方向等需要用到我们方法中的信息。
           不过,系统表中的字段会随着SQL Server版本的变化而变化。比如syscolumns中的type和xtype就是这样的一个变化例子,他们都保存了类型的信息。要让我们的方法适应SQL Server的版本变化要求,就要用到信息结构视图。
           ANSI-92将信息结构视图定义为一组提供系统数据的视图。通过利用该视图,可以将实际系统表从应用程序中隐藏起来。系统表的改变就不会影响到应用程序,这样应用程序就可以独立于数据库厂家和版本。[1]
           ANSI-92和SQL Server支持用三段命名结构引用本地服务器上的对象。ANSI-92术语称为catalog.schema.object,而SQL Server称为database.owner.object。[1]比如我们要找到所有存储过程的所有参数信息,就可以用:
           select * from INFORMATION_SCHEMA.PARAMETERS
    如果要找到某个存储过程的所有参数信息,就是:
           select * from INFORMATION_SCHEMA.PARAMETERS where SPECIFIC_NAME =’Proc1’
    有了信息结构视图,我们的问题就解决了一大半了。下面我们看如何在.NET中实现我们的方法。
     
    3.实现方法
    实现的重点就放在如何根据存储过程名来得到它的所有的参数信息,再根据这些参数信息自动的创建各个参数。为了让这些动作自动化,声明SqlConnection、SqlCommand、SqlParameter的过程,创建各个SqlParameter的过程对用户来说都应该不可见。用户唯一需要提供的就是存储过程的名字,然后就是在调用的时候提供各个参数,甚至连他们的类型都不需要提供。
    31获得和创建存储过程的参数
    如何获得并且创建要调用的存储过程的参数是一个重点,通过信息结构视图我们可以自动的实现这个步骤。
    // 获得和创建存储过程的参数
    private void GetProcedureParameter(params object[] parameters)
    {
            SqlCommand myCommand2 = new SqlCommand();
     
            myCommand2.Connection = this.myConnection;
            myCommand2.CommandText = "select * from INFORMATION_SCHEMA.PARAMETERS where SPECIFIC_NAME='" +this.ProcedureName+ "' order by ORDINAL_POSITION";
     
            SqlDataReader reader = null;
            reader = myCommand2.ExecuteReader();
     
                    // 创建返回参数
                    myParameter = new SqlParameter();
                    myParameter.ParameterName = "@Value";
                    myParameter.SqlDbType = SqlDbType.Int;
                    myParameter.Direction = ParameterDirection.ReturnValue;
     
                    myCommand.Parameters.Add(myParameter);
     
                    int i = 0;
                    // 创建各个参数,在这个地方可以自动的创建SqlParameter的类型,值,方向等属性
                    while(reader.Read())
                    {
                        myParameter = new SqlParameter();
     
                        myParameter.ParameterName = reader["PARAMETER_NAME"].ToString();
                        myParameter.Direction = reader["PARAMETER_MODE"].ToString()=="IN"?ParameterDirection.Input:ParameterDirection.Output;
     
                        switch(reader["DATA_TYPE"].ToString())
                        {
                                case "int" :
                                    if(myParameter.Direction == ParameterDirection.Input)
                                            myParameter.Value = (int)parameters[i];
                                    myParameter.SqlDbType = SqlDbType.Int;
                
                                    break;
                               //...省略了很多具体的类型处理
                                    default : break;
                        }
                        i++;
     
                        myCommand.Parameters.Add(myParameter);
                    }
     
    }
     
     
     
     
    32返回结果数据集、返回值、传出参数集
    创建好存储过程的参数之后,我们就可以调用这个存储过程了。由于在.NET中,常用的返回结果集的类为SqlDataReader和DataSet,而SqlDataReader必须在保持连接的状态下才可以使用,DataSet却不需要。在我们的实现中,连接应该在调用之后就断开,因此采用DataSet来保存返回结果集。
    public SqlResult Call(params object[] parameters)
    {
            // SqlResult是自己定义的用于保存结果数据集、返回值、传出参数集的类
            SqlResult result = new SqlResult();
     
            // 根据需要定义自己的连接字符串
            myConnection = new SqlConnection(ConnectionString);
     
            myCommand = new SqlCommand(this.ProcedureName, myConnection);
            myCommand.CommandType = CommandType.StoredProcedure;
     
            SqlDataAdapter myAdapter = new SqlDataAdapter(myCommand);
     
            myConnection.Open();
            // 获得和创建存储过程的参数,并且设置好值
            GetProcedureParameter(parameters);
     
            myAdapter.Fill(result.dataSet, "Table");
            
            // 获得存储过程的传出参数值和名字对,保存在一个Hashtable中
            GetOutputValue(result);
     
            // 在这里释放各种资源,断开连接
            myAdapter.Dispose();
            myCommand.Dispose();
            myConnection.Close();
            myConnection.Dispose();
     
     
            return result;
    }
    4.进一步工作
           虽然我们在这里的实现是针对SQL Server数据库,但是对于任何提供了信息结构视图,符合ANSI-92标准,或者是提供了元数据的数据库都可以使用这种方法来实现。我们把它封装成一个SqlProcedure类,在需要的时候可以很简单的就调用了存储过程,减少了大量基本上是重复的代码工作。
           为了让SqlProcedure类支持更过的数据类型,在GetProcedureParameter()方法中需要根据自己的需要来分析各个参数的类型、方向、长度、默认值等信息,然后来创建这个参数。基本上任何类型都是能够实现的,甚至连image类型都可以采用这种方式创建。这样这个类就可以很通用,在任何项目中都可以发挥作用。
     
    作者简介:
    刘志波(1979-),男,湖南新化人,硕士,主要研究方向:神经网络与模式识别,办公自动化信息系统
    email:jasper_liu@msn.com
     
    展开全文
  • 两种不同的存储过程调用方法为了突出新方法的优点,首先介绍一下在.NET中调用存储过程的“官方”方法。另外,本文的所有示例程序均工作于SqlServer数据库上,其它情况类似,以后不再一一说明。本文所有例子均采用C#...
  • .NET中统一的存储过程调用方法(收藏) 摘要:在一个项目的开发中,经常会调用数据库中的存储过程。可是,几乎所有存储过程的调用都是同一个模式,主要区别就在于创建的每个参数类型、值等不一样。那么,能不能实现...
  • public SqlParameter[] OBSqlParameter(object[] Parameter) { SqlParameter[] sqlpamtr = new SqlParameter[Parameter.Length]; for (int i = 0; i < Parameter.Length; i...
  • 为了突出新方法的优点,首先介绍一下在.NET中调用存储过程的“官方”方法。另外,本文的所有示例程序均工作于SqlServer数据库上,其它情况类似,以后不再一一说明。本文所有例子均采用C#语言。 要在应用程序中访问...
  • 声明:该文已发表在《计算机应用》第23卷第11期上 摘要:在各种系统开发中,使用存储...那么,可不可以采用一种方法来统一所有的存储过程调用,减少不必要的编程呢?在研究了SQL Server数据库及ASP.NET的基础上,我们实
  • 在各种系统开发中,使用存储过程是一个良好...在存储过程调用中,我们发现存储过程调用都几乎是如下的模式: 1.声明SqlConnection 2.声明SqlCommand,并且设置其Connection属性为刚声明的SqlConnection实例,设
  • 我转载的文章《.NET中统一的存储过程调用方法 》具体实现代码代码中有两个类一个是用来做返回值的载体的用一个DataSet返回查询出的数据,用一个Hashtable返回存储过程的返回值和输出参数.using System;using System...
  • 为了突出新方法的优点,首先介绍一下在.NET中调用存储过程的“官方”方法。另外,本文的所有示例程序均工作于SqlServer数据库上,其它情况类似,以后不再一一说明。本文所有例子均采用C#语言。 要在应用程序中访问...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,261
精华内容 2,904
关键字:

存储过程调用方法