static、まだ使えますか?(二)

3676 ワード

システムを抑えるために、昨日グループはテスト環境で大量の注文データをシミュレートしました.今日の午前中に記録された請求書の利息計算ログを見ると、MySqlExceptionがたくさん見つかりました.
MySql.Data.MySqlClient.MySqlException (0x80004005): 
There is already an open DataReader associated with this Connection which must be closed first.

例:
2017-01-05 00:40:49.891
 /{"BillId":1000012082,"OrderId":"DD201701040002672"}:MySql.Data.MySqlClient.MySqlException (0x80004005): There is already an open DataReader associated with this Connection which must be closed first.
     MySql.Data.MySqlClient.ExceptionInterceptor.Throw(Exception exception)
     MySql.Data.MySqlClient.MySqlCommand.Throw(Exception ex)
     MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
     MySql.Data.MySqlClient.MySqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
     System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior)
     CommonLibrary.CommonOrm.CommonOrm_Dapper.d__11`1.MoveNext()   e:\work\yijia\trunk\CommonLibrary\CommonOrm\CommonOrm_Dapper.cs:  1554
     System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
     System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
     CommonLibrary.CommonOrm.CommonOrm_Dapper.Query[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType)   e:\work\yijia\trunk\CommonLibrary\CommonOrm\CommonOrm_Dapper.cs:  1444
     GateWay.DAL.BillsDal.BillsDal.GetOrdersBillList(String bizOrderId)   e:\work\yijia\trunk\GetWay.DAL\BillsDal\BillsDal.cs:  123
     GateWay.BLL.Bills.BillsBll.GetOrdersBill(String bizOrderId, BillTypeEnum billType)   e:\work\yijia\trunk\GetWay.BLL\Bills\BillsBll.cs:  27
     GateWay.BLL.Bills.PrincipalBillsInterest.InterestBill(t_bills principleBill)   e:\work\yijia\trunk\GetWay.BLL\Bills\PrincipalBillsInterest.cs:  186
     GateWay.BLL.Bills.PrincipalBillsInterest.Interest()   e:\work\yijia\trunk\GetWay.BLL\Bills\PrincipalBillsInterest.cs:  77

解析プログラムにより、dalレイヤのすべての方法は静的であり、静的db接続オブジェクトも含まれていることが分かった.
public class BillsDal
{
    static IDbConnection _conn = ConnUtility.GateWayConntion;
    
    /// 
    ///  (by  code )
    /// 
    ///  Id, 
    /// 
    public static List GetOrdersBillList(string merCode, string bizOrderNo)
    {
        var obj = _conn.Query("select * from t_bills where merCode='" + merCode + "' and orderNo='" + bizOrderNo + "'");
        return obj.ToList();
    }
}

ふと前に整理したblog『static、まだ使えますか?』を思い出し、したがって、問題はこの静的db接続オブジェクトにある.connでは、すべてのクラスのインスタンスが常に1つのdbで接続されているため、同時に発生するとlockデータ操作コードがなく、閉じていないときに接続が確立され、開かれやすくなり、db接続異常が発生します.マルチスレッドをシミュレートしてテストしましたが、確かにそうです.
質問は答えだ!このバグを修復するには、次の2つの方法があります.
  • このstaticを残すならconnフィールドは、同時衝突
  • を制御するためにlockでデータ操作コード(GetOrdersBillList)をロックする
  • クエリーのたびに新しい接続オブジェクトが使用されます.

  • シナリオ解析:1つ目は,オブジェクトが解放(閉じる)されてから再び使用(開く)ことができ,性能が低下し,望ましくないことに関する.2つ目は、実はdal層では、ほとんどのプログラム猿がデータ操作ごとに1つのdb接続で符号化されています.一般的にdalクラスのメンバーをstaticと定義しないため、このようなdb接続異常に遭遇することはありません.私は、staticメソッドを用いて、カプセル化を考慮して、このdb接続オブジェクトを静的フィールドにカプセル化する傾向があり、逆に静的データメンバーによる危険性を無視しています.データ量が小さいとほとんど問題が露呈せず、データ量が大きくなると、同時使用されるリソースが同時に使用され、複数のスレッドインスタンスがその状態を修正すると、同時異常が発生します.
    ということで、相変わらずstaticを使っていると、connは読み取り専用のプライベート属性として扱われます(コードの味は考慮されません):
    static IDbConnection _conn
    {
        get { return ConnUtility.GateWayConntion; }
    }
    

    これにより、このプロパティにアクセスするたびに、新しい接続オブジェクトが返されます.マルチスレッドテストを再シミュレーションし、ok!