8

我有一个 C# 桌面应用程序,它调用各种 SQL Server 存储过程来执行将数据导出和导入 SQL Server 2008 R2 数据库的各种工作。

这些都可以正常工作,没问题。我的应用程序使用所有参数等都可以很好地调用它们。

为了“帮助用户”,我编写了一个按钮来将所有存储过程添加到配置的数据库中。为此,我创建了一个脚本:

USE [%DATABASENAME%]
GO        

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[spMyProc1]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[spMyProc1]
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[spMyProc2]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[spMyProc2]
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[spMyProc3]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[spMyProc3]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[spMyProc1]
        @VariousParams varchar(100),
    @ResultText varchar(4000) OUTPUT
AS
BEGIN
  -- Code removed for brevity
END

GO
--

CREATE PROCEDURE [dbo].[spMyProc2]
        @VariousParams varchar(100),
    @ResultText varchar(4000) OUTPUT
AS
BEGIN
  -- Code removed for brevity
END

GO
--

CREATE PROCEDURE [dbo].[spMyProc3]
        @VariousParams varchar(100),
    @ResultText varchar(4000) OUTPUT
AS
BEGIN
  -- Code removed for brevity
END

GO

当我在 SQL Server Management Studio 中运行它时,它运行良好,完全没有问题。

但是,在我的 C# 应用程序中,抛出了一个异常,并且我收到了大量错误,如下所示:

'GO' 附近的语法不正确。
'GO' 附近的语法不正确。
'GO' 附近的语法不正确。
'GO' 附近的语法不正确。
'GO' 附近的语法不正确。
'GO' 附近的语法不正确。
'CREATE/ALTER PROCEDURE' 必须是查询批处理中的第一条语句。
在此上下文中不能使用具有返回值的 RETURN 语句。
在此上下文中不能使用具有返回值的 RETURN 语句。
'GO' 附近的语法不正确。
必须声明标量变量“@MessageText”。
必须声明标量变量“@ListOfIDsToImport”。
必须声明标量变量“@SourceDataFolder”。

必须声明标量变量“@SequenceNo”。
必须声明标量变量“@UserID”。
必须声明标量变量“@SequenceNo”。
必须声明标量变量“@UserID”。
必须声明标量变量“@ListOfIDsToImport”。
必须声明标量变量“@ListOfIDsToImport”。
必须声明标量变量“@ListOfIDsToImport”。
必须声明标量变量“@MessageText”。
必须声明标量变量“@MessageText”。
必须声明标量变量“@MessageText”。
'GO' 附近的语法不正确。
变量名“@PS_DEFAULT”已被声明。
变量名称“@PS_ERROR_MSG”已被声明。变量名称在查询批处理或存储过程中必须是唯一的。
变量名称“@PS_ERROR_SEVERITY”已被声明。变量名称在查询批处理或存储过程中必须是唯一的。
必须声明标量变量“@SequenceNo”。
'GO' 附近的语法不正确。

(这是 ex.Message 中的内容,被下面代码中的 catch 块捕获)。

我的代码非常简单,如下所示:

    bool retVal = false;
    string command = Properties.Resources.MyApp_StoredProcedures.ToString().Replace("%DATABASENAME%", Properties.Settings.Default.DBName);

    try
    {
        sqlCmd = new SqlCommand(command, csSQLConnection._conn);
        sqlCmd.ExecuteNonQuery();
        retVal = true;
    }
    catch (Exception ex)
    {
        retVal = false;
    }
    finally
    {
        sqlCmd.Dispose();
    }

(上面的替换只是替换了脚本顶部 USE 行中的占位符,当我单步通过该行时,我可以看到它的工作原理)。

所以基本上,我做错了什么,因为 SQL 本身似乎很好?

非常感谢

4

3 回答 3

2

据我所知,您不能设置单个 ADO.NET 对象来执行包含批处理终止符(“GO”)的脚本。您必须做以下两件事之一:

  1. 创建一个 SQL Management Studio 对象并在后台运行它。我知道 SQL Management Studio 的文件夹中有 DLL 可以从 SSMS 中工作。我已经创建了 .NET 代码来为某人打开 SSMS 并即将加载它,但不直接执行它。

  2. 为每个方法执行一个并分解您的 SQL 脚本,以将“GO”语句中的对象数组创建到 List 或类似的内存中。然后使用“foreach”语句遍历该列表,以使用适当的 try/catch 块执行每一个。

于 2013-03-25T16:26:10.360 回答
2

这应该很容易...

摆脱 GO,这是 SSMS 特定的语法,SQL 语言不需要或支持它,而是你应该用 ; 终止你的个人创建脚本 . 让我知道情况如何。

于 2013-03-25T16:23:50.520 回答
0

如果脚本来自 a Stream(例如来自嵌入式资源,通过Assembly.GetManifestResourceStream()、来自 aFileStream或 a MemoryStream)。您可以使用以下内容拆分可以包括的SSMS SQLCMD批处理脚本GO

public IEnumerable<string> GetScriptParts(Stream script)
{
    const string reBatchSeparator = @"^(/\*.\*/)?\s*GO\s*(/\*.*\*/)?\s*(--.*)?$";

    using (StreamReader sr = new StreamReader(script))
    {
        StringBuilder sb = new StringBuilder();
        while(!sr.EndOfStream)
        {
            string line = sr.ReadLine();
            if (!batchSeparator.IsMatch(line))
            {
                sb.AppendLine(line);
            }
            else
            {
                string part = sb.ToString();
                if (!string.IsNullOrEmpty(part))
                {
                    yield return part;
                }
                sb.Clear();
            }
        }

        string part = sb.ToString();
        if (!string.IsNullOrEmpty(part))
        {
            yield return part;
        }
    }
}

该方法ExecuteBatch通过以下方式顺序执行脚本部分SqlCommand

public void ExecuteBatch(SqlConnection conn, Stream script)
{
    foreach (string part in GetScriptParts(script))
    {
        SqlCommand cmd = conn.CreateCommand();

        cmd.CommandText = part;
        cmd.ExecuteNonQuery();
    }
}
于 2016-06-02T14:29:39.427 回答