0

所以基本上我有一个 Soap Web 服务,它可以从 SqlServer 数据库中插入和检索一些数据。

该网络服务使用一个负责数据库内容的单例。

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)

问题可能是因为同时有很多连接到数据库?

可能这不是最好的方法。但是如果你有更好的方法或者有解决这个问题的方法,请说。

4

2 回答 2

2

对于 Db Access,单例模式不是一个好的模式。使用静态方法检索数据。最有可能的是,您的问题是 2 个线程访问了您的实例,而您没有锁定来阻止它。其他要点:

  1. 不要用“SP_”前缀命名你的存储过程
  2. using在处理数据读取器等一次性对象时使用
  3. 使用连接池

我看到您在此处提供的代码中存在各种问题的可能性。例如,如果读取 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
于 2013-07-18T20:11:44.243 回答
0

这是一种方法,放弃了其他答案所暗示的单例想法:

  • 将 Func 传递给您的服务,这是数据访问对象的工厂
  • 让 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);
            }  
       }
       ...
     }
    
于 2013-07-19T16:44:40.147 回答