【问题标题】:Soap WebService with Database access具有数据库访问权限的 Soap WebService
【发布时间】:2013-07-18 14:22:50
【问题描述】:

所以基本上我有一个 Soap Web 服务,它可以从 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)

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

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

【问题讨论】:

  • ASMX 是一项遗留技术,不应用于新开发。 WCF 应该用于 Web 服务客户端和服务器的所有新开发。一个提示:Microsoft 已停用 MSDN 上的 ASMX Forum
  • 另外,在抛出新异常时始终包含原始异常:throw new DBException(message, ex);

标签: c# .net sql-server web-services asmx


【解决方案1】:

单例对于 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

【讨论】:

  • 关于第 1 点,名称“SP_”只是一个示例;)我添加了第二个版本,其中进行了一些更改,我认为这就是您所说的。如果这不是正确的方法,请给我一些例子或类似的东西。
  • 看看我的编辑。如果你坚持使用实例方法的单例 - 添加lock{}。或者,更好的是,将您的方法设为static 而不是lock。我的意思是,将代码从 XXX 移动到 private static void execXXX 并从 XXX 调用它
  • 在 Web 服务中,由于存在多个线程,您在任何情况下都需要锁定单例实例的创建。
【解决方案2】:

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

  • 将 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-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-26
    • 2011-06-07
    • 2013-08-18
    相关资源
    最近更新 更多