このヘージと関連するページ一覧

  • SQLite + System.Data.Linq 導入編
  • SQLite + System.Data.Linq 同時実行制御が不十分な場合
  • SQLite + System.Data.Linq 同時実行制御

  • このページの最終更新日 2020/10/25

    SQLite + System.Data.Linq 同時実行制御

     このページは SQLite + System.Data.Linq 同時実行制御が不完全な場合 の続きのページです。引き続き「データベーススペシャリスト平成26年度春季 午後Ⅰ問2」の会議室の予約システムをもとにして、SQLiteデータベースの演習を行っていきます。

    1、適切な同時実行制御を行うための会議室予約テーブル

     前のページの実装では、同時実行時の処理が不十分であることを説明しました。では、どのような実装にすれば良いのでしょうか。ここでは、データベーススペシャリスト出題例の最終案に沿ってプログラムを実装していきます。最終案の要点は以下の通りです。

    • 会議室予約を行う予約単位をコマと呼び、1コマを30分とする。
    • 会議室予約テーブルでは各コマを0時00分から30分間隔で設定し、予め全てのコマを登録しておく。(1時間で2コマ、1日で48コマ)
    • 該当するコマが予約済みか否かの判定は「予約済フラグ」で識別し、予約済み='Y',予約無し='N'とする。
    • 予約を行う場合は予約するコマ数分UPDATE文で繰り返し処理を行い、全て正常に更新できた場合のみ予約成功としてコミットし、繰り返しの中で一回でも更新が行われなかった場合には予約失敗としてロールバックを行う。


     試験問題に従って、前のページで作成した会議室予約テーブルに「社員番号」と「予約済」カラムを追加します。code.2の12行目のようにデータベースファイル名は「ReservationConferenceRoom2.db」に変更しておきましょう。また、ここでは問題を簡略化して扱うために、会議室番号は1のみ、予約日は「2019/05/14」だけの場合を扱うものとします。

    - code.1 - ファイル名「会議室予約.cs」
    [Table(Name = "会議室予約")]
    public class 会議室予約
    {
        [Column(Name = "会議室番号", IsPrimaryKey = true)]
        public int 会議室番号 { get; set; }
    
        [Column(Name = "予約日", IsPrimaryKey = true)]
        public string 予約日 { get; set; }
    
        [Column(Name = "予約開始時刻", IsPrimaryKey = true)]
        public string 予約開始時刻 { get; set; }
    
        [Column(Name = "予約終了時刻")]
        public string 予約終了時刻 { get; set; }
    
        [Column(Name = "社員番号")]
        public string 社員番号 { get; set; }
    
        [Column(Name = "予約済フラグ")]
        public string 予約済フラグ { get; set; }
    
        public DateTime StartDateTime()
        {
            return DateTime.Parse(this.予約日 + " " + this.予約開始時刻);
        }
        public DateTime EndDateTime()
        {
            return DateTime.Parse(this.予約日 + " " + this.予約終了時刻);
        }
    }     
        
    - code.2 - ファイル名「DatabaseWrapper.cs」
    using System.Data.Linq;
    using System.Data.SQLite;
    using IO = System.IO;
        
    public class DatabaseWrapper
    {
        private DataContext _dataContext;
        
        public DatabaseWrapper()
        {
            
            string dbFilePath = IO.Directory.GetParent(System.Reflection.Assembly.GetExecutingAssembly().Location).FullName + @"\ReservationConferenceRoom2.db";
    
            var builder = new SQLiteConnectionStringBuilder()
            {
                DataSource = dbFilePath,
                Version = 3
            };
    
            var connect = new SQLiteConnection(builder.ToString());
            _dataContext = new DataContext(connect);
    
            if (IO.File.Exists(dbFilePath)) { return;}
    
            connect.Open();
            SQLiteCommand createCommand = connect.CreateCommand();
            createCommand.CommandText = "CREATE TABLE 会議室予約(会議室番号 INT,予約日 TEXT,予約開始時刻 TEXT,予約終了時刻 TEXT,社員番号 TEXT,予約済フラグ TEXT,PRIMARY KEY(会議室番号,予約日,予約開始時刻))";
            createCommand.ExecuteNonQuery();
    
            SQLiteCommand insertCommand = connect.CreateCommand();
            insertCommand.CommandText = "INSERT INTO 会議室予約(会議室番号,予約日,予約開始時刻,予約終了時刻,予約済フラグ) VALUES(@会議室番号,@予約日,@予約開始時刻,@予約終了時刻,@予約済フラグ)";
    
            DateTime certainDay = DateTime.Parse("2019/05/14 00:00");
            for (int iSpan = 0; iSpan < 47; iSpan++)
            {
                insertCommand.Parameters.Add(new SQLiteParameter("@会議室番号", 1));
                insertCommand.Parameters.Add(new SQLiteParameter("@予約日", certainDay.ToString("yyyy/MM/dd")));
                insertCommand.Parameters.Add(new SQLiteParameter("@予約開始時刻", (certainDay + TimeSpan.FromMinutes(30 * iSpan)).ToString("HH:mm")));
                insertCommand.Parameters.Add(new SQLiteParameter("@予約終了時刻", (certainDay + TimeSpan.FromMinutes(30 * (iSpan + 1))).ToString("HH:mm")));
                insertCommand.Parameters.Add(new SQLiteParameter("@予約済フラグ", "N"));
                insertCommand.ExecuteNonQuery();
            }
        }
    
        public Table<会議室予約> GetReservationConferenceRoomTable()
        {
            return _dataContext.GetTable<会議室予約>();
        }
    }
    

     code.2の34行目から42行目で、0時0分から23時30分までの時間帯を30分毎に47分割した予約時間帯の行を挿入します。また、デフォルトではまだ予約されていないことを示すために、予約済フラグを"N"としておきます。

    会議室予約システムのコマの予約状況表示画面

    - pic.1 -

    2、会議室予約テーブルの更新処理

    会議室予約システムのコマの予約状況表示画面

    - pic.2 -

    public partial class FormEdit : Form
    {
        public FormEdit()
        {
            InitializeComponent();
            btnOk.Click += new EventHandler(OKButtonClick);
            btnCancel.Click += new EventHandler(CancelButtonClick);
        }
    
        public void OKButtonClick(object sender, EventArgs e)
        {
            try
            {
                var objDb = new DatabaseWrapper();
                var objReservationConferenceRoomTable = objDb.GetReservationConferenceRoomTable();
    
                int conferenceRoomId = int.Parse(tbConferenceRoomId.Text);
                string reservationDate = dtpReservationDate.Value.ToString("yyyy/MM/dd");
                string startHHmm = DecimalValueToStringD2(udStartHour.Value) + ":" + DecimalValueToStringD2(udStartMinute.Value);
                string endHHmm = DecimalValueToStringD2(udEndHour.Value) + ":" + DecimalValueToStringD2(udEndMinute.Value);
    
                DateTime startDateTime = DateTime.Parse(reservationDate + " " + startHHmm);
                DateTime endDateTime = DateTime.Parse(reservationDate + " " + endHHmm);
    
                if (endDateTime <= startDateTime)
                {
                    throw new Exception("予約終了時刻は予約開始時刻よりも後の時刻を指定していなければなりません。");
                }
    
                if (objReservationConferenceRoomTable.ToList().Where(m => m.会議室番号 == conferenceRoomId && startDateTime <= m.StartDateTime() && m.EndDateTime() <= endDateTime && m.予約済フラグ == "Y").Any())
                {
                    throw new Exception("予約しようとした時間の中に、既に予約されている時間が含まれています。");
                }
    
                Task.Delay(3000).Wait();
    
                IEnumerable<会議室予約> toReserveSpanList = objReservationConferenceRoomTable.ToList().Where(m => m.会議室番号 == conferenceRoomId && startDateTime <= m.StartDateTime() && m.EndDateTime() <= endDateTime);
                foreach(会議室予約 toReserveSpan in toReserveSpanList)
                {
                    toReserveSpan.社員番号 = tbEmpoyeeId.Text;
                    toReserveSpan.予約済フラグ = "Y";
                }
                objDb.SubmitChanges();
    
                this.DialogResult = DialogResult.OK;
                this.Close();
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    
        private string DecimalValueToStringD2(Decimal udValue)
        {
            return ((int)udValue).ToString("D2");
        }
    
        public void CancelButtonClick(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.Cancel;
            this.Close();
        }
    }