编程测试SQL Server连接的最佳方法是什么?

99

我需要开发一个单一的例程,每5分钟触发一次,以检查SQL服务器列表(10到12个)是否正在运行。

有没有一种简单的方式可以用最少的代码和SQL操作要求从C#中“ping”一个SQL服务器?


2
仅仅ping服务器是不够的,服务器可能在运行但SQL实例已经停止。创建一个实际的ado.net连接到实例是最好的选择。 - Rory
2
正如您所知,MS-SQL和SQL-server之间有很大的区别,特别是在涉及端口和Ping的情况下。多么民主啊,每个人都必须使用相同的标签。没有选择!您可以添加另一个标签,没有问题,但是为什么要删除我选择的标签!!! - backslash17
2
MS-SQL就是SQL Server - 产品名称就是SQL Server。 - marc_s
11
SQL Server可能指:
  • 任何实现结构化查询语言的数据库服务器
  • Microsoft SQL Server,微软的一个特定实现数据库服务器
  • Sybase SQL Server,由Sybase开发的关系型数据库服务器。 ------- 如您所见,SQL Server有多种含义,这就是我使用MSSQL标签的原因。
- backslash17
2
@backslash17 是的,而且如果你在 sql-server 标签上悬停鼠标,你会发现它是 Microsoft 特定的。我也会说 mssql 和 mysql 等等... - NappingRabbit
有点晚了,但是mssql不应该被别名为sql-server吗? - vikarjramun
11个回答

108

当连接服务器停止或暂停时,我在使用EF时遇到了困难,并提出了同样的问题。为了完整性起见,这里是代码。

/// <summary>
/// Test that the server is connected
/// </summary>
/// <param name="connectionString">The connection string</param>
/// <returns>true if the connection is opened</returns>
private static bool IsServerConnected(string connectionString)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        try
        {
            connection.Open();
            return true;
        }
        catch (SqlException)
        {
            return false;
        }
    }
}

15
在这种情况下不需要使用connection.Close();,因为using语句会在结束时自动关闭连接。 - user915331
4
你应该将 try-catch 语句放在 using 语句的外面。(https://dev59.com/7m455IYBdhLWcg3wAvNI或https://dev59.com/1G025IYBdhLWcg3wOzE9)。 - aloisdg
1
@aloisdg - 这取决于情况,他们是在测试是否可以连接到数据库,只有在特殊情况下无法连接时才返回false。还是说他们想要吞噬与SQL连接构造不正确有关的异常?我知道这是有区别的,因为我刚刚编写了上述内容的代码。 - brumScouse

89

执行 SELECT 1 并检查执行 ExecuteScalar 后是否返回 1。


1
这很好,不需要在数据库中创建任何对象,但是我需要有一个数据库和一个用户来进行查询。我只想知道MSSQL端口的服务是否正常。无论如何,你已经解决了近75%的问题。这将是一个选项。 - backslash17
6
无论如何,您都有主数据库 :) 检查 SQL Server 是否正在运行的最清洁的方法是连接到它。要连接,您无论如何都需要数据库和登录信息。所有其他解决方案(如 ping SQL Server 端口)无法保证 SQL Server 正在正确运行,并且任何人都可以连接到它。 - Andrew Bezzub
我在意图点赞的时候不小心点了踩。一天后我才发现这个错误,所以我无法再纠正我的错误了。 - Giorgos Betsos
2
@GiorgosBetsos - 我帮你修正了错误 :-D - Kunal B.

15

请查看GitHub上的以下项目:https://github.com/ghuntley/csharp-mssql-connectivity-tester

try
{
    Console.WriteLine("Connecting to: {0}", AppConfig.ConnectionString);
    using (var connection = new SqlConnection(AppConfig.ConnectionString))
    {
        var query = "select 1";
        Console.WriteLine("Executing: {0}", query);

        var command = new SqlCommand(query, connection);

        connection.Open();
        Console.WriteLine("SQL Connection successful.");

        command.ExecuteScalar();
        Console.WriteLine("SQL Query execution successful.");
    }
}
catch (Exception ex)
{
    Console.WriteLine("Failure: {0}", ex.Message);
}

7

建立与数据库的连接不是能够自动完成这个过程吗?如果数据库没有启动,你将无法建立连接。


实际上,只需使用ADO net进行连接 - 如果在超时期限内没有响应,则表示数据库不可用。您无需发出查询来确定这一点。 - Dan Diplo
为了使用ADO.NET进行检查,我需要一个用户,我只想检查服务是否正在运行,无论数据库是否正在运行都没有问题。我需要像telnet SMTP服务器一样的东西。不需要拥有用户即可获得响应。 - backslash17
3
“登录失败用户…”的响应已足以确认两件事情:1)机器正在运行,2)服务正在运行。如果您遇到连接超时,则说明服务未运行/无法正常工作。 - Rory
@Rory:好观点!只需要在try/catch块中检查错误即可。谢谢! - backslash17
@backslash17 请检查“用户登录失败”的错误代码是否有单独的错误代码或其他帮助您确定它的内容。通过异常消息来区分错误是非常糟糕的做法。 - Andrew Bezzub

2

在端口1433上查找一个开放的监听器(默认端口)。如果在创建tcp连接后收到任何响应,则服务器可能已经启动。


你知道吗,我最初是在2010年写的。今天,我会尝试实际连接服务器


根据Joel Coehorn的建议,你已经尝试过tcping [http://www.elifulkerson.com/projects/tcping.php]了吗?它是一个独立的可执行文件,允许你在每个指定的时间间隔内进行ping操作。虽然它不是用C#编写的。另外...如果目标机器有防火墙,我不确定这是否会起作用...嗯... - Ashish Gupta

2
Joel Coehorn建议的内容,您是否已经尝试过名为tcping的实用程序。我知道这不是您以编程方式执行的操作。它是一个独立的可执行文件,允许您在每个指定的时间间隔内进行ping测试。虽然它不是用C#编写的。此外……如果目标机器有防火墙,我不确定它是否会起作用。
[我对这个网站还比较新,不小心将其添加为评论,现在将其添加为回答。如果我在这里重复评论(作为评论和回答),请告诉我是否可以在这里完成。我无法在这里删除评论。]

1
public static class SqlConnectionExtension
{
    #region Public Methods

    public static bool ExIsOpen(
        this SqlConnection connection, MessageString errorMsg = null)
    {
        if (connection == null) { return false; }
        if (connection.State == ConnectionState.Open) { return true; }

        try
        {
            connection.Open();
            return true;
        }
        catch (Exception ex) { errorMsg?.Append(ex.ToString()); }
        return false;
    }

    public static bool ExIsReady(
        this SqlConnection connction, MessageString errorMsg = null)
    {
        if (connction.ExIsOpen(errorMsg) == false) { return false; }
        try
        {
            using (var command = new SqlCommand("select 1", connction))
            { return ((int)command.ExecuteScalar()) == 1; }
        }
        catch (Exception ex) { errorMsg?.Append(ex.ToString()); }
        return false;
    }

    #endregion Public Methods
}

public class MessageString : IDisposable
{
    #region Protected Fields

    protected StringBuilder _messageBuilder = new StringBuilder();

    #endregion Protected Fields

    #region Public Constructors

    public MessageString()
    {
    }

    public MessageString(int capacity)
    {
        _messageBuilder.Capacity = capacity;
    }

    public MessageString(string value)
    {
        _messageBuilder.Append(value);
    }

    #endregion Public Constructors

    #region Public Properties

    public int Length {
        get { return _messageBuilder.Length; }
        set { _messageBuilder.Length = value; }
    }

    public int MaxCapacity {
        get { return _messageBuilder.MaxCapacity; }
    }

    #endregion Public Properties

    #region Public Methods

    public static implicit operator string(MessageString ms)
    {
        return ms.ToString();
    }

    public static MessageString operator +(MessageString ms1, MessageString ms2)
    {
        MessageString ms = new MessageString(ms1.Length + ms2.Length);
        ms.Append(ms1.ToString());
        ms.Append(ms2.ToString());
        return ms;
    }

    public MessageString Append<T>(T value) where T : IConvertible
    {
        _messageBuilder.Append(value);
        return this;
    }

    public MessageString Append(string value)
    {
        return Append<string>(value);
    }

    public MessageString Append(MessageString ms)
    {
        return Append(ms.ToString());
    }

    public MessageString AppendFormat(string format, params object[] args)
    {
        _messageBuilder.AppendFormat(CultureInfo.InvariantCulture, format, args);
        return this;
    }

    public MessageString AppendLine()
    {
        _messageBuilder.AppendLine();
        return this;
    }

    public MessageString AppendLine(string value)
    {
        _messageBuilder.AppendLine(value);
        return this;
    }

    public MessageString AppendLine(MessageString ms)
    {
        _messageBuilder.AppendLine(ms.ToString());
        return this;
    }

    public MessageString AppendLine<T>(T value) where T : IConvertible
    {
        Append<T>(value);
        AppendLine();
        return this;
    }

    public MessageString Clear()
    {
        _messageBuilder.Clear();
        return this;
    }

    public void Dispose()
    {
        _messageBuilder.Clear();
        _messageBuilder = null;
    }

    public int EnsureCapacity(int capacity)
    {
        return _messageBuilder.EnsureCapacity(capacity);
    }

    public bool Equals(MessageString ms)
    {
        return Equals(ms.ToString());
    }

    public bool Equals(StringBuilder sb)
    {
        return _messageBuilder.Equals(sb);
    }

    public bool Equals(string value)
    {
        return Equals(new StringBuilder(value));
    }

    public MessageString Insert<T>(int index, T value)
    {
        _messageBuilder.Insert(index, value);
        return this;
    }

    public MessageString Remove(int startIndex, int length)
    {
        _messageBuilder.Remove(startIndex, length);
        return this;
    }

    public MessageString Replace(char oldChar, char newChar)
    {
        _messageBuilder.Replace(oldChar, newChar);
        return this;
    }

    public MessageString Replace(string oldValue, string newValue)
    {
        _messageBuilder.Replace(oldValue, newValue);
        return this;
    }

    public MessageString Replace(char oldChar, char newChar, int startIndex, int count)
    {
        _messageBuilder.Replace(oldChar, newChar, startIndex, count);
        return this;
    }

    public MessageString Replace(string oldValue, string newValue, int startIndex, int count)
    {
        _messageBuilder.Replace(oldValue, newValue, startIndex, count);
        return this;
    }

    public override string ToString()
    {
        return _messageBuilder.ToString();
    }

    public string ToString(int startIndex, int length)
    {
        return _messageBuilder.ToString(startIndex, length);
    }

    #endregion Public Methods
}

1

感谢@27k1

https://dev59.com/OnE95IYBdhLWcg3wI6ft#9943871

我在这里添加了一些代码:

/// <summary>
        /// Test that the server is connected
        /// </summary>
        /// <param name="connectionString">The connection string</param>
        /// <returns>true if the connection is opened</returns>
        public static (bool isConnected, string sqlErrorMessage) IsServerConnected(string connectionString)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                try
                {
                    connection.Open();
                    return (true, "");
                }
                catch (SqlException ex)
                {
                    return (false, ex.Message);
                }
            }
        }

然后调用

string connectionString = $@"Server={txtServerName.Text.Trim()};Database={txtDatabaseName.Text.Trim()};
                                            User Id={txtLogin.Text.Trim()};Password={txtPassword.Text.Trim()};";
            var response = IsServerConnected(connectionString);
            if (response.isConnected)
            {
                lblConnectionState.Text = "✔";
                txtSqlConnectionError.Text = "";
            }
            else
            {
                lblConnectionState.Text = "❌";
                txtSqlConnectionError.Text = response.sqlErrorMessage;
            }

0

这是基于 @peterincumbria 的答案的我的版本:

using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return await dbContext.Database.CanConnectAsync(cToken);

我正在使用Observable进行轮询健康检查,并处理函数的返回值。 这里不需要try-catch,因为: enter image description here

0

我通常通过打开连接来实现这一点,但有些情况下,通过Open进行简单测试会导致AccessViolationException


using (SqlConnection db = new SqlConnection(conn))
{    
  db.Open(); // -- Access Violation caused by invalid Server in Connection String
}

所以我按照Joel Coehoorn的建议,在打开之前进行了TCP检查。C#代码可能是:

string targetAddress = "";
try
{
  targetAddress = GetServerFromConnectionString();
  IPAddress ipAddress = Dns.GetHostEntry(targetAddress).AddressList[0];
  IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 1433);

  using (TcpClient tcpClient = new TcpClient())
  {
       tcpClient.Connect(ipEndPoint);           
  }
            
}
catch (Exception ex)
{
    LogError($"TestViaTcp to server {targetAddress} failed '{ex.GetType().Name}': {ex.Message}");
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接