2013-07-18 3 views
0

기본적으로 나는 SqlServer 데이터베이스에서 일부 데이터를 삽입 및 검색하는 Soap webservice가 있습니다.데이터베이스 액세스 비누 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=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) 

받는 동시에 많은 연결에 있기 때문에 문제가 될 수있다 데이터 베이스?

아마도 이것이 최선의 방법은 아닙니다. 그러나 더 나은 방법이 있거나이 문제에 대한 해결책이 있다면 말하십시오.

+2

ASMX는 기존 기술이므로 새로운 개발에 사용해서는 안됩니다. WCF는 웹 서비스 클라이언트 및 서버의 모든 새로운 개발에 사용해야합니다. 한 가지 힌트 : Microsoft는 MSDN에서 [ASMX 포럼] (http://social.msdn.microsoft.com/Forums/en-US/asmxandxml/threads)을 퇴역 시켰습니다. –

+1

또한 새 예외를 throw 할 때 항상 원래 예외가 포함됩니다. 'throw new DBException (message, ex);' –

답변

2

싱글 톤은 Db 액세스에 좋은 패턴이 아닙니다. 데이터 검색에 정적 메서드를 사용하십시오. 대부분의 경우 문제는 2 개의 스레드가 인스턴스에 액세스 했으므로 잠금을 방지 할 수 있습니다. 다른 점 :

  1. 는 DataReader를 일회용 객체를 처리하는 동안
  2. 를 사용하여 연결이

을 풀링 "SP_"접두어를 사용하여 저장 프로 시저

  • 사용 using 이름을 지정하지 내가 잠재적 참조 여기서 제시 한 코드의 모든 문제에 대해 예를 들어 데이터 판독기 오류를 읽으면 자원을 정리하지 않습니다. 당신은 예외 처리의 더 나은 제어를 할 경우 대신 usingtry-catch-finally을 사용하는 것입니다,

    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을 수행합니다. // 의사 코드

    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 
    
  • +0

    포인트 1에 대해서는 "SP_"이름이 예제와 같습니다.) 다음과 같이 두 번째 버전을 추가했습니다. 제 생각에 몇 가지 변화가 당신이 말한 것이 었습니다. 이것이 적절한 방법이 아니라면, 저에게 몇몇보기 또는 저것 같이 저를 지적하십시오. –

    +0

    내 편집을보세요. 인스턴스 메소드로 싱글 톤을 주장한다면 - lock {}을 추가하십시오. 아니면, 더 나은 방법은'정적'과'잠금'하지 않습니다. 제 말은'XXX'의 코드를'private static void execXXX'로 옮기고'XXX'에서 호출하는 것입니다. –

    +0

    웹 서비스에서 다중 인스턴스 때문에 싱글 톤 인스턴스 생성을 둘러싼 잠금이 필요합니다 스레드. –

    0

    여기에 다른 대답에서 알 수 있듯이 싱글 생각을 포기 접근,의 : 데이터 액세스 개체에 대한 공장 서비스에 Func을 전달

    • 되세요 DBAccess는 사용중인 블록을 사용하는 IDisposable을 구현합니다.

      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); 
           } 
          } 
          ... 
      } 
      
    관련 문제