normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

データベースアクセス時のアンチパターン

SQL Serverに限らず、DB接続時にやってはいけない事として、「DB関連オブジェクトの共有」という物があります。
ちょっと纏まって無いですが、後で例に挙げるコードだとこんな例外がでます。

  • $exception {"操作が無効です。接続は閉じています。"} System.Exception {System.InvalidOperationException}
  • $exception {"接続が閉じられていません。現在の接続の状態は '接続中\" です。。"} System.Exception {System.InvalidOperationException}
  • $exception {"ExecuteReader は、コマンドに割り当てられた接続が保留状態であるローカルのトランザクションにあるとき、トランザクション オブジェクトを持つコマンドが必要です。コマンドの Transaction プロパティがまだ初期化されていません。"} System.Exception {System.InvalidOperationException}
  • $exception {"SubmitChanges の呼び出し中は、その操作を実行できません。"} System.Exception {System.InvalidOperationException}

例外方の言ってる事としては、「DBに接続するオブジェクトを共有してるんじゃねーよ、トランザクションつくれねぇだろう」という事の様です。
もちっとまとめないと…orz

コード例

やっている事は以下の二つです。

  • 共通のDBオブジェクトを作成
  • 並列実行用のオブジェクト作成

で、上の二つをとりあえず実行してみた感じ。

class InfoDto
{
    public string Name { get; set; }
    public int SleepTime { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        string connection = ConfigurationManager.ConnectionStrings["DotNetSampleDBConnectionString"].ConnectionString;
        DataClasses1DataContext dataContext = new DataClasses1DataContext(connection);
        dataContext.Log = Console.Out;
        dataContext.CommandTimeout = 1;

        //並列実行用の関数作成
        WaitCallback func = delegate(object obj)
        {
            InfoDto infoDto = obj as InfoDto;
            Console.WriteLine("db access start: {0}, sleep={1}", infoDto.Name, infoDto.SleepTime);
            Thread.Sleep(infoDto.SleepTime);
            dataContext.SampleTable1.First().COMMENT = infoDto.Name + " がupdate したよー";
            dataContext.SubmitChanges();
            Console.WriteLine("db access finish: {0}, sleep={1}", infoDto.Name, infoDto.SleepTime);
        };

        List<WaitHandle> waitHandles = new List<WaitHandle>();
        waitHandles.Add(func.BeginInvoke(new InfoDto { Name = "No.1", SleepTime = 300 }, null, null).AsyncWaitHandle);
        waitHandles.Add(func.BeginInvoke(new InfoDto { Name = "No.2", SleepTime = 2500 }, null, null).AsyncWaitHandle);
        waitHandles.Add(func.BeginInvoke(new InfoDto { Name = "No.3", SleepTime = 3500 }, null, null).AsyncWaitHandle);
        waitHandles.Add(func.BeginInvoke(new InfoDto { Name = "No.4", SleepTime = 1500 }, null, null).AsyncWaitHandle);
        
        //ここでまとめて実行
        WaitHandle.WaitAll(waitHandles.ToArray());

        Console.WriteLine("------------ end ------------");
        Console.ReadLine();
    }
}