C#のみで疑似データベースを作成し、ロールバックを実装してみた

最近は業務でデータベース(以下DB)を使っているのですが、DBをC#で実装したらどうなるかなと思い試してみました。
トランザクションとかを実装すると大変そうだったので、ロールバックのイメージのみ実装しています。

ロールバックをTruncateとInsertの組み合わせで実装しているので、実際の業務でデータを保持するために使うなら、もう少し詳細設計を考えたほうがいいかもしれないです。
今回は、「例外が出たらこんな感じでロールバックしてデータを戻すのか」という部分を参考にしてもらえればと思います。

補足ですが、ロールバック用のデータをToArray()で保持しているのは、ToArray()の方がToList()よりもパフォーマンスが良い場合が多いからです。

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleCommitAndRollback
{
    class Program
    {
        static void Main(string[] args)
        {
            var db = new DB();
            CreateData(db);

            //ここから例外が発生する可能性のある処理
            SomeTasks(db);

            //中のデータを確認する
            foreach (var table in db.Select())
            {
                Console.WriteLine($"ID = {table.Id}, Field1 = {table.Field1}, Field2 = {table.Field2}");
            }
        }

        private static void CreateData(DB db)
        {
            Enumerable.Range(0, 3)
                .ToList()
                .ForEach(num => db.Insert(new DataTable(num)));
        }

        private static DB SomeTasks(DB db)
        {
            var tempDataTables = db.Select().ToArray();//ロールバック用データを保持

            try
            {
                db.Insert(new DataTable(5));
                throw new Exception();
                db.Insert(new DataTable(6));
                return db;
            }
            catch
            {
                //ここがロールバック処理
                db.Truncate();
                db.Insert(tempDataTables);
                return db;
            }
        }

        private class DB
        {
            private List<DataTable> _tables;

            public DB()
            {
                _tables = new List<DataTable>();
            }

            public IReadOnlyCollection<DataTable> Select()
            {
                return _tables;
            }

            public void Insert(DataTable table)
            {
                _tables.Add(table);
            }

            public void Insert(IReadOnlyCollection<DataTable> tables)
            {
                _tables.AddRange(tables);
            }

            public IReadOnlyCollection<DataTable> Update(DataTable table)
            {
                var target = _tables.SingleOrDefault(target => target.Id == table.Id);
                _tables.Remove(target);
                _tables.Add(table);
                return _tables;
            }

            public void Truncate()
            {
                _tables.RemoveAll(_ => true);
            }
        }

        private class DataTable
        {
            public int Id { get; }
            public string Field1 { get; set; }
            public int Field2 { get; set; }

            public DataTable(int num)
            {
                Id = num;
                Field1 = $"Value{num}";
                Field2 = num * 10;
            }
        }
    }
}

この実行結果は以下のようになり、例外前に追加したdb.Insert(new DataTable(5));はデータに反映されていないことが分かります。

コメント

タイトルとURLをコピーしました