#region License Information /* HeuristicLab * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Data; using System.Collections.Generic; using System.Text; using System.Linq; using System.Threading; using HeuristicLab.DataAccess.Interfaces; namespace HeuristicLab.DataAccess.ADOHelper { public abstract class DataAdapterBase where AdapterT : new() where RowT : System.Data.DataRow where ObjT : IPersistableObject, new() { private ISession session; private ITableAdapterWrapper dataAdapter; protected DataAdapterBase( ITableAdapterWrapper dataAdapter) { this.dataAdapter = dataAdapter; } protected AdapterT Adapter { get { return dataAdapter.TransactionalAdapter; } } protected ITableAdapterWrapper DataAdapterWrapper { get { return dataAdapter; } } public ISession Session { get { return this.session; } set { if (!(value is Session)) throw new Exception("Can only bind to ADO session"); this.session = value; this.dataAdapter.Session = value as Session; } } public object InnerAdapter { get { return this.Adapter; } } #region Abstract methods protected abstract RowT ConvertObj(ObjT obj, RowT row); protected abstract ObjT ConvertRow(RowT row, ObjT obj); #endregion protected delegate IEnumerable Selector(); protected delegate object TransactionalAction(); protected ObjT Convert(RowT row, ObjT obj) { try { obj = ConvertRow(row, obj); return obj; } catch (DeletedRowInaccessibleException) { return default(ObjT); } catch (RowNotInTableException) { return default(ObjT); } } protected RowT FindSingleRow(Selector selector) { RowT row = default(RowT); IEnumerable found = selector(); if (found.Count() == 1) row = found.First(); return row; } protected ObjT FindSingle(Selector selector) { return (ObjT)doInTransaction( delegate() { RowT row = FindSingleRow(selector); ObjT result; if (row != null) { ObjT obj = new ObjT(); obj = Convert(row, obj); result = obj; } else { result = default(ObjT); } return result; }); } private ICollection FindMultiple(Selector selector, int from, int size) { return (ICollection)doInTransaction( delegate() { IEnumerable found = selector(); if (from > 0 && size > 0) found = found.Skip(from).Take(size); IList result = new List(); foreach (RowT row in found) { ObjT obj = new ObjT(); obj = Convert(row, obj); if (obj != null) result.Add(obj); } return result; }); } protected ICollection FindMultiple(Selector selector) { return FindMultiple( selector, 0, -1); } protected virtual RowT GetRowById(Guid id) { return FindSingleRow( delegate() { return dataAdapter.FindById(id); }); } protected object doInTransaction(TransactionalAction action) { ITransaction trans = session.GetCurrentTransaction(); bool transactionExists = trans != null; if (!transactionExists) { trans = session.BeginTransaction(); } try { object result = action(); if (!transactionExists && trans != null) { trans.Commit(); } return result; } catch (Exception e) { if (!transactionExists && trans != null) { trans.Rollback(); } throw e; } } protected virtual void doUpdate(ObjT obj) { if (obj != null) { RowT row = null; if (obj.Id != Guid.Empty) { row = GetRowById(obj.Id); } else { obj.Id = Guid.NewGuid(); } if (row == null) { row = dataAdapter.InsertNewRow(obj); } ConvertObj(obj, row); dataAdapter.UpdateRow(row); } } public void Update(ObjT obj) { try { doInTransaction( delegate() { doUpdate(obj); return true; }); } catch (DBConcurrencyException ex) { DataRow row = ex.Row; RowT current = GetRowById(obj.Id); if (current != null) { //find out changes for (int i = 0; i < row.ItemArray.Length; i++) { if (!row[i, DataRowVersion.Current].Equals( row[i, DataRowVersion.Original])) { current[i] = row[i]; } } ConvertRow(current, obj); //try updating again Update(obj); } //otherwise: row was deleted in the meantime - nothing to do } } public virtual ObjT GetById(Guid id) { return FindSingle(delegate() { return dataAdapter.FindById(id); }); } public virtual ICollection GetAll() { return new List( FindMultiple( new Selector(dataAdapter.FindAll))); } public virtual ICollection GetAll(int from, int size) { //note - this base implementation is inefficient, //consider overriding the implementation //in derived adapters (based on a query (SQL LIMIT)) return new List( FindMultiple( new Selector(dataAdapter.FindAll), from, size)); } protected virtual bool doDelete(ObjT obj) { bool success = false; if (obj != null) { RowT row = GetRowById(obj.Id); if (row != null) { row.Delete(); dataAdapter.UpdateRow(row); success = true; } } return success; } public bool Delete(ObjT obj) { try { return (bool)doInTransaction( delegate() { return doDelete(obj); }); } catch (DBConcurrencyException) { RowT current = GetRowById(obj.Id); if (current != null) { ConvertRow(current, obj); //try deleting again return Delete(obj); } else { //row has already been deleted return false; } } } } }