[英]How to deploy stored procedure via C# code?
我有一个像
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) 逻辑的存储过程?
附注。 我知道我可以通过 c# 在另一个 SQL 脚本中删除存储过程,但我想在一个脚本中完成它。 另请注意,我有 SQL Server 2014,而不是 2016 等较新版本(因为我的公司,我知道它很糟糕)
使用动态 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 。
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
}
考虑使用SQL Server 管理对象。 与SqlClient
不同, SMO
包括识别GO
批处理分隔符的方法。 SMO
作为NuGet 包提供。
下面是一个示例控制台应用程序,它使用Microsoft.SqlServer.Management.Common.ServerConnection.ExecuteNonQuery
方法执行带有GO
批处理分隔符的脚本。
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;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.