更新:2007 年 11 月
本主题适用于:
| 版本 | Visual Basic | C# | C++ | Web Developer | 
|---|---|---|---|---|
| 速成版 | .gif) | .gif) | .gif) | .gif) | 
| 标准版 | .gif) | .gif) | .gif) | .gif) | 
| 专业团队版 | .gif) | .gif) | .gif) | .gif) | 
表格图例:
| .gif) | 适用 | 
| .gif) | 不适用 | 
| .gif) | 默认情况下隐藏的一条或多条命令。 | 
本主题包含演示如何调试多层应用程序的示例代码,并介绍从驻留在客户端或中间层应用程序中的应用程序代码进入在 SQL Server 2005 中运行的数据库对象的代码所需的调试步骤。
应用程序层与数据库层之间的转换要求在目标层中设置断点;否则,在尝试单步执行目标层时,只执行代码而不会停止。但是数据库层内部的 T-SQL 代码和 SQL CLR 代码之间的转换不需要断点就可以在这两者之间单步执行。
下面示例使用 AdventureWorks 数据库,并在不同层及不同语言之间来回执行单步调试。该示例旨在阐释这些转换,显然不是实际的业务方案。
示例调用三个存储过程:
- DeleteCurrency 是一个 SQL CLR 存储过程,它删除具有给定货币代码的货币。 
- DeleteCurrency_T_SQL 执行同一操作,但是它是用 T-SQL 编写的。 
- DeleteCurrencyDriver 调用上述两个存储过程,它有一个输入参数,该参数指定要删除的货币的代码。 
应用程序代码调用这三个存储过程,并传入货币代码参数。从两种不同的上下文(即从 DeleteCurrencyDriver 和直接从应用程序)调用两个“非驱动程序”存储过程。在第一个上下文中,可以从 DeleteCurrencyDriver 单步进入其他两个存储过程。从应用程序调用它们时,不能直接单步进入它们,而必须在存储过程中设置断点。
调试数据库应用程序
- 在一个新的 SQL Server 项目中,建立一个到 AdventureWorks 数据库的连接。有关更多信息,请参见如何:连接到数据库。 
- 使用下面第一个示例部分中的代码创建 T-SQL 存储过程,并将其命名为 DeleteCurrency_T_SQL。有关此步骤或此过程中任何步骤的更多信息,请参见 如何:使用 SQL Server 项目类型进行开发。 
- 使用下面第二个示例部分中的代码创建一个 SQL CLR 存储过程,并将其命名为 DeleteCurrency.cs。 
- 使用下面第三个示例部分中的代码创建一个 SQL CLR 存储过程,并将其命名为 DeleteCurrencyDriver。 
- 在“调试”菜单上单击“开始”,将这些更改编译并部署到 AdventureWorks 数据库中。 
- 在每个存储过程中至少设置一个断点。从本机代码或托管代码无法进入并单步执行存储过程。 
- 在 Visual Studio 中创建新的控制台项目。 
- 将第四个示例中的代码粘贴到文本编辑器中。 
- 在对存储过程的每个调用前后放置断点。 
- 按 F5 运行应用程序。 
- 逐句通过不同的模块。 
- 尝试移除一些断点来查看尝试在不同层及不同语言之间进出的效果。 
- 若要完成调试,请从 Visual Studio“调试”菜单中清除所有断点,然后按 F5。 
示例
本节包含 T-SQL 存储过程的代码。
CREATE PROCEDURE dbo.DeleteCurrency_T_SQL
    (
        @CurrencyCode nvarchar(3)
    )
AS
    SET NOCOUNT ON
    DELETE Sales.Currency 
    WHERE CurrencyCode = @currencyCode 
    RETURN
此代码包含从驱动程序存储过程调用的 SQL CLR 存储过程的代码。
using System;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;
 
public partial class StoredProcedures
{
    [SqlProcedure]
    public static void DeleteCurrency(SqlString currencyCode)
    {
        string sCmd = "DELETE Sales.Currency WHERE CurrencyCode = '" + currencyCode.Value + "'";
        SqlConnection conn = new SqlConnection("Context Connection=True");
        conn.Open();
        SqlCommand  DeleteCurrencyCommand = new  SqlCommand( sCmd , conn);
        DeleteCurrencyCommand.ExecuteNonQuery();
    }
}
此代码包含调用其他过程的 SQL CLR 驱动程序过程的代码。此存储过程从应用程序层进行调用。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
 
public partial class StoredProcedures
{
    [SqlProcedure]
    public static void DeleteCurrencyDriver(SqlString CurrencyCode)
    {
        string sCommand = "DELETE Sales.Currency WHERE CurrencyCode = '" + CurrencyCode.Value + "'";
        SqlConnection conn = new SqlConnection("Context Connection=True");
        conn.Open();
        SqlCommand DeleteCurrencyCommand = new SqlCommand(sCommand, conn);
        DeleteCurrencyCommand.ExecuteNonQuery();
 
        // Now execute a T-SQL stored procedure.
        DeleteCurrencyCommand.CommandType = CommandType.StoredProcedure;
        DeleteCurrencyCommand.CommandText = "DeleteCurrency_T_SQL";
        // Fill the parameters collection based upon stored procedure.
        SqlParameter workParam = null;
        workParam = DeleteCurrencyCommand.Parameters.Add("@CurrencyCode", SqlDbType.NChar, 3);
        DeleteCurrencyCommand.Parameters["@CurrencyCode"].Value = "ESC";
        try { DeleteCurrencyCommand.ExecuteNonQuery(); }
        catch { }
 
        // Now execute a CLR stored procedure.
        DeleteCurrencyCommand.CommandText = "DeleteCurrency";
        try { DeleteCurrencyCommand.ExecuteNonQuery(); }
        catch { }
    }
};
此代码包含将调用驱动程序存储过程(同时直接调用 T-SQL 和 SQL CLR 存储过程)的应用程序代码。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
 
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
            builder.DataSource = <server>;
            builder.IntegratedSecurity = true;
            builder.InitialCatalog = <database>;
 
            SqlConnection SqlConnection1 = new SqlConnection(builder.ConnectionString);
            SqlConnection1.Open();
 
            SqlCommand procCommand = new SqlCommand();
 
            procCommand.CommandText = "DeleteCurrencyDriver";
            procCommand.CommandType = CommandType.StoredProcedure;
            procCommand.Connection = SqlConnection1;
            // Fill parameters collection for the stored procedure.
            SqlParameter workParam = null;
            workParam = procCommand.Parameters.Add("@CurrencyCode", SqlDbType.NChar, 3);
            procCommand.Parameters["@CurrencyCode"].Value = "ESC";
            
            try { procCommand.ExecuteNonQuery(); }
            catch (SqlException e) { DumpException(e); }
 
            procCommand.CommandText = "DeleteCurrency";
            try { procCommand.ExecuteNonQuery(); }
            catch (SqlException e) { DumpException(e); }
 
            procCommand.CommandText = "DeleteCurrency_T_SQL";
            try { procCommand.ExecuteNonQuery(); }
            catch (SqlException e) { DumpException(e); }
 
            SqlConnection1.Close();
        }
        static void DumpException(SqlException e)
       {
            string errorMessages = "";
            for (int i = 0; i < e.Errors.Count; i++)
           {
                errorMessages += "Index #" + i + "\n" +
                       "Message: " + e.Errors[i].Message + "\n" +
                       "LineNumber: " + e.Errors[i].LineNumber + "\n" +
                       "Source: " + e.Errors[i].Source + "\n" +
                       "Procedure: " + e.Errors[i].Procedure + "\n";
            }
            System.Diagnostics.EventLog log = new System.Diagnostics.EventLog();
            log.Source = "My Application";
            log.WriteEntry(errorMessages);
            Console.WriteLine("An exception occurred. Please contact your system administrator.");
        }
    }
}