所以基本上我有一个Soap webservice,可以从SqlServer数据库中插入和检索一些数据。
webservice使用负责数据库内容的Singleton。
public class Service : System.Web.Services.WebService
{
private DBAccess dbaccess;
public Service()
{
dbaccess = DBAccessLocalhost.GetInstance();
}
[WebMethod]
public List<Profile> XXX(Guid a, uint b, DateTime c)
{
return dbaccess.XXX(a, b, c);
}
...
}
访问数据库的单例。它有很多方法基本上都可以做到这一点。
public class DBAccessLocalhost : DBAccess
{
private static DBAccess instance = null;
private string connectionString;
public static DBAccess GetInstance()
{
if (instance == null)
instance = new DBAccessLocalhost();
return instance;
}
private DBAccessLocalhost()
{
connectionString = "Data Source=localhost;Initial Catalog=DBName;Integrated Security=True;Max Pool Size=2000;Pooling=false";
}
public override void XXX(Guid a, uint b, DateTime c)
{
SqlCommand cmd;
SqlDataReader dr;
string strSql = "SP_Name";
SqlConnection conn;
conn = new SqlConnection(connectionString);
try
{
conn.Open();
cmd = new SqlCommand(strSql, conn);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@a", a.ToString());
cmd.Parameters.AddWithValue("@b", (int)b);
cmd.Parameters.AddWithValue("@c", c);
dr = cmd.ExecuteReader();
while (dr.Read() && dr.HasRows)
{
//Do stuff...
}
dr.Close();
}catch (Exception ex)
{
throw new DBError("Errors: " + ex.Message);
}
finally
{
conn.Close();
}
}
第二版:
public class DBAccessLocalhost : DBAccess
{
private static DBAccess instance = null;
private string connectionString;
public static DBAccess GetInstance()
{
if (instance == null)
instance = new DBAccessLocalhost();
return instance;
}
private DBAccessLocalhost()
{
connectionString = "Data Source=localhost;Initial Catalog=DBName;Integrated Security=True;Max Pool Size=2000;Pooling=true";
}
public override void XXX(Guid a, uint b, DateTime c)
{
string strSql = "SP_Name";
using (SqlConnection conn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand(strSql, conn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@a", a.ToString());
cmd.Parameters.AddWithValue("@b", (int)b);
cmd.Parameters.AddWithValue("@c", c);
try
{
conn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
//....
}
}
}
catch (Exception ex)
{
throw new DBError("Error: " + ex.Message);
}
finally
{
conn.Close();
}
}
}
问题是有时我会遇到这个例外:
DBAccessWebSerice,System.ServiceModel.FaultException: Server was unable to process
request. ---> Errors: ExecuteNonQuery requires an open and available
Connection. The connection's current state is closed (Sometimes says connecting).
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime
operation, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway,
ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage
methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
问题可能是因为数据库同时存在多个连接?
可能这不是最好的办法。但是,如果您有更好的方法来解决这个问题,请说明一下。
答案 0 :(得分:2)
Singleton不是Db Access的好模式。使用静态方法检索数据。最有可能的是,您的问题是2个线程访问了您的实例,并且您无法阻止它。 其他要点:
using
我发现您在此处提供的代码中存在各种问题的可能性。例如,如果读取datareader错误,则不会清除资源。
这是一种方式:
public override void XXX(Guid a, uint b, DateTime c)
{
string strSql = "SP_Name";
lock (lockObject) { // lock the code for the duration of execution so it is thread safe now (because you in singleton)
// But this is bad for DB access !!!! What if SP executes long - other threads have to wait
// Use simple static methods for data access and no `lock`
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(strSql, conn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@a", a.ToString());
cmd.Parameters.AddWithValue("@b", (int)b);
cmd.Parameters.AddWithValue("@c", c);
// no need for any `try-catch` here because `using` is that and `finally`
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
//....
}
}
}
conn.Close();
}
}
}
其他选项,如果您希望更好地控制异常处理,则使用try-catch-finally
代替using
。然后,最后,您将执行另一个try-catch
来清理资源。
//伪代码
try
connection
command
datareader
catch (Exception ex)
prepare user message
log or
throw/throw new, etc // depends. This is Webservice, may be log and response with error message
finally
try
if datareader alive
close datareader
dispose datareader
catch // do nothing here
try
if command alive
dispose command
catch // do nothing here
try
if connection alive
close connection
dispose connection
catch // do nothing here
答案 1 :(得分:0)
这是一种方法,放弃单身人士的想法,正如另一个答案所暗示的那样:
让DBAccess实现IDisposable并使用using块
public class Service : System.Web.Services.WebService
{
private Func<DBAccess> getDBAccess;
public Service(Func<DBAccess> getDBAccess)
{
this.getDBAccess = getDBAccess;
}
[WebMethod]
public List<Profile> XXX(Guid a, uint b, DateTime c)
{
using(var dbaccess = this.getDBAccess())
{
return dbaccess.XXX(a, b, c);
}
}
...
}