C# 具有数据库访问功能的soapwebservice

C# 具有数据库访问功能的soapwebservice,c#,.net,sql-server,web-services,asmx,C#,.net,Sql Server,Web Services,Asmx,所以基本上我有一个Soap Web服务,它可以从SqlServer数据库中插入和检索一些数据 webservice使用一个负责DB内容的单例 public class Service : System.Web.Services.WebService { private DBAccess dbaccess; public Service() { dbaccess = DBAccessLocalhost.GetInstance(); } [W

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

webservice使用一个负责DB内容的单例

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=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)
问题可能是因为同时有多个到数据库的连接


也许这不是最好的办法。但是,如果您有更好的方法或解决此问题的方法,请说。

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

  • 不要将存储过程命名为带有“SP_”前缀
  • 在处理诸如datareader之类的一次性对象时使用
  • 使用连接池
  • 我看到您在这里介绍的代码中可能存在各种各样的问题。例如,如果读取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
    ,而不是使用
    。然后,在最后,您将执行另一个
    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
    

    这里有一种方法,放弃了单身的想法,正如另一个答案所建议的那样:

    • 将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);
              }  
         }
         ...
       }
      
      公共类服务:System.Web.Services.WebService
      {
      私有Func getDBAccess;
      公共服务(Func getDBAccess)
      {
      this.getDBAccess=getDBAccess;
      }
      [网络方法]
      公共列表XXX(Guid a、uint b、日期时间c)
      {
      使用(var dbaccess=this.getDBAccess())
      {
      返回dbaccess.XXX(a,b,c);
      }  
      }
      ...
      }
      

    关于第1点,名称“SP_”只是一个例子;)我添加了第二个版本,其中有一些更改,我认为这就是您所说的。如果这不是正确的方法,请给我举一些例子或类似的例子。看看我的编辑。如果您坚持使用实例方法的singleton-add
    lock{}
    。或者,更好的方法是使您的方法
    静态
    且不
    锁定
    。我的意思是,将代码从
    XXX
    移动到
    private static void execXXX
    并从
    XXX
    调用它。在web服务中,由于多线程,您在任何情况下都需要对单例实例的创建进行锁定。ASMX是一种传统技术,不应用于新的开发。WCF应用于web服务客户端和服务器的所有新开发。一个提示:Microsoft已停用MSDN上的。此外,在引发新异常时始终包含原始异常:
    throw new DBException(message,ex)
    
    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);
            }  
       }
       ...
     }