C#检测离线模式(SQL Server连接)

时间:2017-12-30 08:41:25

标签: c# sql .net exception-handling offline-mode

我正在开发一个连接到SQL Server的C#应用​​程序。如果网络连接中断,应用程序应该能够进入“只读模式”(离线模式)并且只能从本地数据库中读取数据。现在,我正试图弄清楚如何检测断开连接:

public int executeNonQuery(string query, List<SqlParameter> parameters)
{
        int result;

        using (SqlConnection sqlConnection = new SqlConnection(ConnectionString))
        {
            tryOpenSqlConnection(sqlConnection);

            using (SqlCommand cmd = new SqlCommand(query, sqlConnection))
            {
                if (parameters != null)
                {
                    cmd.Parameters.AddRange(parameters.ToArray());
                }
                result = cmd.ExecuteNonQuery();
            }

            sqlConnection.Close(); 
        }

        return result;
}

private void tryOpenSqlConnection(SqlConnection sqlConnection)
{
        try
        {
            sqlConnection.Open();
        }
        catch (SqlException se)
        {
            if (se.Number == 26)
            {
                catchOfflineMode(se);
            }

            throw se;
        }
    }

//...

private void catchOfflineMode(SqlException se)
{
    Console.WriteLine("SqlException: " + se.Message);
    Console.WriteLine("Setting offline mode...");

    //...
}

我考虑过使用SQL错误代码来检测连接丢失。但问题是,有时我只有在已经建立SqlConnection之后才会获得例外,例如在执行命令期间。我得到的最后一个例外是

  

错误代码121 - 信号量超时期限已过期

因此,我必须检查可能与丢失网络连接有关的每个错误代码。

编辑:我还想过捕获每个SqlException然后检查以太网连接(例如ping服务器)以检查异常是否来自丢失的连接。

有更好的方法吗?

1 个答案:

答案 0 :(得分:0)

我通过创建一个名为

的简单助手类来提出自己的解决方案
  

ExternalServiceHandler.cs

用作外部服务调用的代理,以在操作失败后检测应用程序的联机和脱机状态。

using System;
using System.Threading;
using System.Threading.Tasks;
using Application.Utilities;

namespace Application.ExternalServices
{
    class ExternalServiceHandler: IExternalServiceHandler
    {
        public event EventHandler OnlineModeDetected;
        public event EventHandler OfflineModeDetected;

        private static readonly int RUN_ONLINE_DETECTION_SEC = 10;
        private static ExternalServiceHandler instance;
        private Task checkOnlineStatusTask;
        private CancellationTokenSource cancelSource;
        private Exception errorNoConnection;

        public static ExternalServiceHandler Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new ExternalServiceHandler();
                }
                return instance;
            }
        }

        private ExternalServiceHandler()
        {
            errorNoConnection = new Exception("Could not connect to the server.");
        }

        public virtual void Execute(Action func)
        {
            if (func == null) throw new ArgumentNullException("func");

            try
            {
                func();
            }
            catch
            {
                if(offlineModeDetected())
                {
                    throw errorNoConnection;
                }
                else
                {
                    throw;
                } 
            }
        }

        public virtual T Execute<T>(Func<T> func)
        {
            if (func == null) throw new ArgumentNullException("func");

            try
            {
                return func();
            }
            catch
            {
                if (offlineModeDetected())
                {
                    throw errorNoConnection;
                }
                else
                {
                    throw;
                }
            }
        }

        public virtual async Task ExecuteAsync(Func<Task> func)
        {
            if (func == null) throw new ArgumentNullException("func");

            try
            {
                await func();
            }
            catch
            {
                if (offlineModeDetected())
                {
                    throw errorNoConnection;
                }
                else
                {
                    throw;
                }
            }
        }

        public virtual async Task<T> ExecuteAsync<T>(Func<Task<T>> func)
        {
            if (func == null) throw new ArgumentNullException("func");

            try
            {
                return await func();
            }
            catch
            {
                if (offlineModeDetected())
                {
                    throw errorNoConnection;
                }
                else
                {
                    throw;
                }
            }
        }

        private bool offlineModeDetected()
        {
            bool isOffline = false;
            if (!LocalMachine.isOnline())
            {
                isOffline = true;
                Console.WriteLine("-- Offline mode detected (readonly). --");

                // notify all modues that we're in offline mode
                OnOfflineModeDetected(new EventArgs());

                // start online detection task
                cancelSource = new CancellationTokenSource();
                checkOnlineStatusTask = Run(detectOnlineMode, 
                    new TimeSpan(0,0, RUN_ONLINE_DETECTION_SEC), 
                    cancelSource.Token);
            }
            return isOffline;
        }

        private void detectOnlineMode()
        { 
            if(LocalMachine.isOnline())
            {
                Console.WriteLine("-- Online mode detected (read and write). --");

                // notify all modules that we're online
                OnOnlineModeDetected(new EventArgs());

                // stop online detection task
                cancelSource.Cancel();
            }
        }

        public static async Task Run(Action action, TimeSpan period, CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                await Task.Delay(period, cancellationToken);

                if (!cancellationToken.IsCancellationRequested)
                {
                    action();
                }    
            }
        }

        protected virtual void OnOfflineModeDetected(EventArgs e)
        {
            OfflineModeDetected?.Invoke(this, e);
        }
        protected virtual void OnOnlineModeDetected(EventArgs e)
        {
            OnlineModeDetected?.Invoke(this, e);
        }
    }
}

LocalMachine.isOnline()方法如下所示:

namespace Application.Utilities
{
    public class LocalMachine
    {
        // ... //

        public static bool isOnline()
        {
            try
            {
                using (var client = new WebClient())
                {
                    string serveraddress = AppSettings.GetServerHttpAddress();
                    using (var stream = client.OpenRead(serveraddress))
                    {
                        return true;
                    }
                }
            }
            catch
            {
                return false;
            }
        }

        // ... //
}

每次进行外部服务调用时都可以使用帮助程序类。在以下示例中,ExternalServiceHandler执行SQL非查询:

public async Task<int> executeNonQueryAsync(string query)
{
    return await ExternalServiceHandler.Instance.ExecuteAsync(async () =>
    {
        return await DBManager.executeNonQueryAsync(query);
    });
}

解决方案对我来说很好。如果您有任何更好的想法,请告诉我。