C# 检测离线模式(SQL 服务器连接)
C# detect offline mode (SQL Server connection)
我正在开发一个连接到 SQL 服务器的 C# 应用程序。如果网络连接中断,应用程序应该能够进入 "read-only mode"(离线模式)并且只从本地数据库读取数据。现在,我正在尝试弄清楚如何检测断开连接:
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
已经建立之后我才会得到异常,例如命令执行期间。我得到的最后一个例外是
Error Code 121 - The semaphore timeout period has expired
所以,我必须检查每一个可能与网络连接断开有关的错误代码。
编辑:我还考虑过捕获每个 SqlException
然后检查以太网连接(例如 ping 服务器)以检查异常是否来自丢失的连接与否。
有更好的方法吗?
我通过创建一个名为
的简单助手 class 提出了自己的解决方案
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;
}
}
// ... //
}
每次调用外部服务时都可以使用助手class。在下面的示例中,ExternalServiceHandler 执行了一个 SQL 非查询:
public async Task<int> executeNonQueryAsync(string query)
{
return await ExternalServiceHandler.Instance.ExecuteAsync(async () =>
{
return await DBManager.executeNonQueryAsync(query);
});
}
该解决方案对我来说效果很好。如果您有更好的想法,请告诉我。
我正在开发一个连接到 SQL 服务器的 C# 应用程序。如果网络连接中断,应用程序应该能够进入 "read-only mode"(离线模式)并且只从本地数据库读取数据。现在,我正在尝试弄清楚如何检测断开连接:
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
已经建立之后我才会得到异常,例如命令执行期间。我得到的最后一个例外是
Error Code 121 - The semaphore timeout period has expired
所以,我必须检查每一个可能与网络连接断开有关的错误代码。
编辑:我还考虑过捕获每个 SqlException
然后检查以太网连接(例如 ping 服务器)以检查异常是否来自丢失的连接与否。
有更好的方法吗?
我通过创建一个名为
的简单助手 class 提出了自己的解决方案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;
}
}
// ... //
}
每次调用外部服务时都可以使用助手class。在下面的示例中,ExternalServiceHandler 执行了一个 SQL 非查询:
public async Task<int> executeNonQueryAsync(string query)
{
return await ExternalServiceHandler.Instance.ExecuteAsync(async () =>
{
return await DBManager.executeNonQueryAsync(query);
});
}
该解决方案对我来说效果很好。如果您有更好的想法,请告诉我。