#質問
ある要件を満たすコードをより柔軟にしたいです。
要件は以下です。
あるデータベースのテーブルAのデータをBに移し、Bを空にしたい。このとき、以下の順に処理を行う。
1 トランザクションを開始
2 Bのレコードをすべて削除
3 Aからデータを抽出し、Bに移す(データは似通っていて、データの変換は単純)
4 Aのレコードをすべて削除
5 トランザクションを終了
1'途中で例外的状況に陥ったらロールバックしてログを出力して異常終了
以下、捕捉となります。
データベース操作はEntity Frameworkによって行う
このとき、以下の実装を行いました。
設定値などはところどころ省略しています。注目していただきたいのはDataAToBクラスです。
class DataAToB { private readonly ISetting setting; private readonly IMapper<A, B> mapper; public DataAToB(ISetting setting, IMapper<A, B> mapper) { this.setting = setting; this.mapper = mapper; } // データを移す処理はこれ public void Do() { // 1 using (var context = new EntityFrameworkOrdinaryContext(setting)) using (var subContext = new EntityFrameworkOrdinaryContext(setting)) using (var transaction = context.Database.BeginTransaction()) { try { // 2 context.Bs.Delete(); // 3 foreach (var dataOfA in subContext.As) { // 捕捉:マッピングは絶対成功する var dataForB = this.mapper.Map(dataOfA); context.Bs.Add(dataForB); } // 4 context.As.Delete(); // 5 context.SaveChanges(); transaction.Commit(); } catch (Exception) { // 1' transaction.Rollback(); throw new AToBFailureException(); } } } } class EntityFrameworkOrdinaryContext : DbContext { public EntityFrameworkOrdinaryContext(ISetting setting) : base(null) { } public DbSet<A> As { get; } public DbSet<B> Bs { get; } } interface ISetting { } interface IMapper<TRaw, TMapped> { TMapped Map(TRaw raw); } class A { } class B { } class AToBFailureException : Exception { //... }
しかし、このコードに改善の余地があるような気がしています。
以下が気になっているところです。
1.コンテキストクラスを直接生成している
2.Bからデータを削除し、BからAにデータを移し、Aのデータを削除するという具体的な一連の処理を直接やっている。
3.さらにトランザクション管理までしている。
4.データベースコンテキストクラスや、EntityFrameworkに直接依存している(DbContext,DbSetなど)
AからBでなくBからA、もっと言えばあらゆるエンティティからエンティティに対して、さらに言えば機能を分割してトランザクションがないパターンにも対応でき、あらゆるデータのストレージ間に対して汎用的に適用できるようなインターフェースと実装を書けたりしないものかと思い質問しました。必要に駆られているので教えてほしいといった旨ではございません。目的はコードをよくすることと、良いコードをかけるようになるための経験を積むことです。このコードは改善可能か、どういった観点から改善の方針を定めるのか、逆にこのままで十分なのかなど、皆様の意見をなんでも頂ければと思います。
よろしくお願いいたします。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。