我有一个类似的存储过程
IF (OBJECT_ID('sp_InsertDevice', 'P') IS NOT NULL)
DROP PROCEDURE sp_InsertDevice
GO
CREATE PROCEDURE sp_InsertDevice
@serialNumber NVARCHAR(8),
@modelName NVARCHAR(40),
@userId INT
AS
BEGIN
INSERT INTO Device (SerialNumber, ModelName, UserID)
VALUES (@serialNumber, @modelName, @userId)
SELECT CAST(SCOPE_IDENTITY() AS INT);
END
和用于部署它的C#方法:
protected virtual async Task<bool> DeployStoredProcedure(string storedProcedureName)
{
try
{
var dir = Directory.GetFiles(this.StoredProceduresPath);
string storedProceduresPath = Directory.GetFiles(this.StoredProceduresPath).Where(x => x.Contains(storedProcedureName)).First();
string storedProcedureScriptFull = File.ReadAllText(storedProceduresPath);
SqlCommand insertProcedureCommand = new SqlCommand(storedProcedureScriptFull, this.SqlConnection)
{
CommandType = CommandType.Text,
CommandTimeout = this.CommandTimeout
};
await this.EnsureConnectionOpened();
await insertProcedureCommand.ExecuteNonQueryAsync();
return true;
}
catch (Exception exception)
{
this.SqlConnection.Close();
ExceptionDispatchInfo.Capture(exception).Throw();
return false;
}
}
通常来说,它将存储过程脚本读取为字符串,然后尝试像通常的SQL查询一样执行它。一切正常,直到到达
await insertProcedureCommand.ExecuteNonQueryAsync();
并引发异常
'GO'附近的语法不正确
CREATE / ALTER PROCEDURE”必须是查询批处理中的第一条语句。
我注意到,如果存储过程没有这部分
IF (OBJECT_ID('sp_InsertDevice', 'P') IS NOT NULL)
DROP PROCEDURE sp_InsertDevice
GO
不会引发异常,并且该过程将成功部署。因此问题可以表述为:如何通过C#代码部署包含(IF EXISTS-DROP)逻辑的存储过程?
PS。我知道我可以在另一个SQL脚本中通过c#删除存储过程,但我想在一个脚本中执行。还请注意,我有SQL Server 2014,而不是像2016这样的较新版本,等等(由于我的公司,我知道那很烂)
答案 0 :(得分:3)
使用动态SQL的解决方法:
IF (OBJECT_ID('sp_InsertDevice', 'P') IS NOT NULL)
DROP PROCEDURE sp_InsertDevice
EXEC(
'CREATE PROCEDURE sp_InsertDevice
@serialNumber nvarchar(8),
@modelName nvarchar(40),
@userId int
AS
BEGIN
INSERT INTO Device (SerialNumber, ModelName, UserID)
VALUES (@serialNumber, @modelName, @userId)
SELECT CAST(SCOPE_IDENTITY() AS INT);
END');
不幸的是,您必须将存储过程中的每个'
加倍。 T-SQL尚不支持here-strings i T-SQL 。
答案 1 :(得分:1)
GO不是有效的SQL命令,用于将SQL分成由SQL Server管理器在服务器上顺序执行的部分。
但是,使用SQL Server生成的脚本分发数据库方案非常容易。
因此,我将自己生成的SQL脚本拆分为“ GO”关键字,并一一执行。
类似这样的东西(这是一些“很旧的”代码的副本,因此您应该使用var等对其进行清理):
Regex regex = new Regex("GO\r\n",RegexOptions.Singleline);
ArrayList updateCommands = new ArrayList(regex.Split(updateScript));
using (SqlConnection con = GetNewConnection()) {
con.Open();
foreach(string commandText in updateCommands) {
if (string.IsNullOrWhiteSpace(commandText)) continue;
using (SqlCommand cmd = new SqlCommand(commandText, con)) {
cmd.ExecuteNonQuery();
}
} // foreach
}
答案 2 :(得分:0)
请考虑使用SQL Server Management Objects。与SqlClient
不同,SMO
包含用于识别GO
批量分隔符的方法。 SMO
作为NuGet package可用。
下面是一个示例控制台应用程序,该应用程序使用GO
方法使用Microsoft.SqlServer.Management.Common.ServerConnection.ExecuteNonQuery
批处理分隔符执行脚本。
using System;
using System.Data;
using System.Data.SqlClient;
using Microsoft.SqlServer.Management.Common;
class Example
{
static void Main(string[] args)
{
var script = @"
IF (OBJECT_ID('sp_InsertDevice', 'P') IS NOT NULL)
DROP PROCEDURE sp_InsertDevice
GO
CREATE PROCEDURE sp_InsertDevice
@serialNumber nvarchar(8),
@modelName nvarchar(40),
@userId int
AS
BEGIN
INSERT INTO Device (SerialNumber, ModelName, UserID)
VALUES (@serialNumber, @modelName, @userId)
SELECT CAST(SCOPE_IDENTITY() AS INT);
END
";
try
{
using (var connection = new SqlConnection("Data Source=YourServer;Integrated Security=SSPI;Initial Catalog=YourDatabase"))
{
var serverConnection = new ServerConnection(connection);
connection.Open();
serverConnection.ExecuteNonQuery(script);
}
}
catch
{
throw;
}
}
}