using System;
using System.Data;
using System.Data.Common;
using System.Collections;
using System.Collections.Generic;
using Light.Model;
namespace Light
{
/// <summary>
/// Data Access Object.
/// </summary>
public abstract class Dao : IDisposable
{
#region - Housekeeping -
private static LRUCache<Table> cache = new LRUCache<Table>(50);
private bool disposed = false;
/// <summary>
/// Database connection.
/// </summary>
protected IDbConnection cn;
/// <summary>
/// Current transaction.
/// </summary>
protected IDbTransaction trans;
/// <summary>
/// Gets or sets the size of the cache.
/// Note: The cache is shared between all Dao objects.
/// </summary>
public static int CacheSize
{
get { return cache.Size; }
set { cache.Size = value; }
}
/// <summary>
/// Creates a new data access object.
/// </summary>
protected Dao()
{
}
/// <summary>
/// Creates a new data access object with given database connection.
/// </summary>
/// <param name="connection">database connection</param>
protected Dao(IDbConnection connection)
{
cn = connection;
}
/// <summary>
/// Dispose.
/// </summary>
/// <param name="disposing">disposing</param>
protected virtual void Dispose(bool disposing)
{
if(disposing)
Dispose();
}
/// <summary>
/// If this method is called while this Dao object is in the
/// middle of a transaction, the transaction is rolled back.
/// </summary>
public virtual void Dispose()
{
if(!disposed)
{
if(InTransaction)
Rollback();
cn = null;
GC.SuppressFinalize(this);
disposed = true;
}
}
/// <summary>
/// Gets or sets the database connection.
/// </summary>
public IDbConnection Connection
{
get { return cn; }
set { cn = value; }
}
/// <summary>
/// Gets a table model for given type.
/// </summary>
/// <param name="type">type</param>
/// <returns>table model</returns>
private Table GetTable(Type type)
{
Table table = cache.Get(type.FullName);
if(table == null)
{
table = TableFactory.Build(type);
table.CachedObject = new CachedCommands();
cache.Put(type.FullName, table);
}
return table;
}
#endregion
#region - Transaction management -
/// <summary>
/// True if this data access object is in the middle of a transaction.
/// </summary>
public bool InTransaction
{
get { return trans != null; }
}
/// <summary>
/// Starts a new transaction.
/// Throws a LightException if there is an active transaction
/// because parallel transactions are currently not supported.
/// </summary>
public void Begin()
{
if(InTransaction)
throw new LightException("Currently, Light does not support parallel transactions. " +
"There is already a transaction associated with the current connection. " +
"Please use Commit or Rollback methods to end the current transaction.");
trans = cn.BeginTransaction();
}
/// <summary>
/// Commits the currently active transaction.
/// Does nothing if there is no active transaction.
/// </summary>
public void Commit()
{
if(InTransaction)
{
trans.Commit();
DisposeTransaction();
}
}
/// <summary>
/// Rolls back the currently active transaction.
/// Does nothing if ther is no active transaction.
/// </summary>
public void Rollback()
{
if(InTransaction)
{
trans.Rollback();
DisposeTransaction();
}
}
/// <summary>
/// Properly disposes of a transaction.
/// </summary>
private void DisposeTransaction()
{
if(InTransaction)
{
trans.Dispose();
trans = null;
}
}
#endregion
#region - Insert -
/// <summary>
/// Inserts objects in the given array as objects of given type.
/// </summary>
/// <param name="type">data type</param>
/// <param name="list">list of objects to insert</param>
/// <returns>number of records affected</returns>
public int Insert(Type type, ICollection list)
{
if(list == null || list.Count == 0)
return 0;
if(type == null)
throw new LightException("Cannot insert objects of NULL type.");
Table table = null;
Command cmd = null;
IDbCommand dbcmd = null;
AbstractColumn autoIncr = null;
Trigger[] triggers = null;
TriggerContext context = null;
int result = 0;
bool begin = !InTransaction;
try
{
table = GetTable(type);
if(table.ReadOnly)
throw new DeclarationException("Table " + table.Name + " is marked readonly.");
triggers = table.Triggers;
context = new TriggerContext(this);
autoIncr = table.AutoIncrementColumn;
if(begin)
Begin();
context.TriggeringAction = Actions.BeforeInsert;
FireTriggers(context, list, triggers);
CachedCommands cc = (CachedCommands) table.CachedObject;
if(cc.Insert == null)
cc.Insert = GetInsertCommand(table);
cmd = cc.Insert;
dbcmd = CreateDbCommand(cmd);
dbcmd.Transaction = trans;
dbcmd.Prepare();
foreach(object target in list)
{
if(target == null)
continue;
if(autoIncr != null)
{
object seqValue = NextSequenceValue(table);
if(seqValue != null)
autoIncr.Write(target, seqValue);
}
AssignParams(dbcmd, table, cmd, target);
result += dbcmd.ExecuteNonQuery();
if(autoIncr != null)
{
object autoVal = AutoIncrementedValue(table, dbcmd);
if(autoVal != null)
autoIncr.Write(target, autoVal);
}
}
context.TriggeringAction = Actions.AfterInsert;
FireTriggers(context, list, triggers);
if(begin)
Commit();
}
catch(DeclarationException)
{
throw;
}
catch(TriggerException)
{
if(begin)
Rollback();
throw;
}
catch(DbException)
{
if(begin)
Rollback();
throw;
}
catch(Exception e)
{
if(begin)
Rollback();
throw new LightException(e.Message, e);
}
finally
{
if(dbcmd != null)
dbcmd.Dispose();
}
return result;
}
/// <summary>
/// Inserts objects in the given list.
/// </summary>
/// <typeparam name="T">type that objects in list should be treated as</typeparam>
/// <param name="list">list of objects to insert</param>
/// <returns>number of records affected</returns>
public int Insert<T>(ICollection list)
{
return Insert(typeof(T), list);
}
/// <summary>
/// Inserts given object as object of given type.
/// </summary>
/// <param name="type">data type</param>
/// <param name="obj">object to insert</param>
/// <returns>number of records affected</returns>
public int Insert(Type type, object obj)
{
if(obj == null)
return 0;
return Insert(type, new object[] { obj });
}
/// <summary>
/// Inserts given object.
/// </summary>
/// <param name="obj">object to insert</param>
/// <returns>number of records affected</returns>
public int Insert(object obj)
{
if(obj == null)
return 0;
return Insert(obj.GetType(), new object[] { obj });
}
/// <summary>
/// Inserts given object.
/// </summary>
/// <typeparam name="T">type that given object should be treated as</typeparam>
/// <param name="obj">object to insert</param>
/// <returns>number of records affected</returns>
public int Insert<T>(object obj)
{
return Insert(typeof(T), obj);
}
#endregion
#region - Update -
/// <summary>
/// Updates objects in given array as objects of given type.
/// </summary>
/// <param name="type">data type</param>
/// <param name="list">list of objects to update</param>
/// <returns>number of records affected</returns>
public int Update(Type type, ICollection list)
{
if(list == null || list.Count == 0)
return 0;
if(type == null)
throw new LightException("Cannot update objects of NULL type.");
Table table = null;
Command cmd = null;
IDbCommand dbcmd = null;
Trigger[] triggers = null;
TriggerContext context = null;
int result = 0;
bool begin = !InTransaction;
try
{
table = GetTable(type);
if(table.ReadOnly)
throw new DeclarationException("Table " + table.Name + " is marked readonly.");
if(table.KeyColumns.Length == 0)
throw new DeclarationException("Table " + table.Name + " must have at least one primary key column.");
triggers = table.Triggers;
context = new TriggerContext(this);
if(begin)
Begin();
context.TriggeringAction = Actions.BeforeUpdate;
FireTriggers(context, list, triggers);
CachedCommands cc = (CachedCommands) table.CachedObject;
if(cc.Update == null)
cc.Update = GetUpdateCommand(table);
cmd = cc.Update;
dbcmd = CreateDbCommand(cmd);
dbcmd.Transaction = trans;
dbcmd.Prepare();
foreach(object target in list)
{
if(target == null)
continue;
AssignParams(dbcmd, table, cmd, target);
result += dbcmd.ExecuteNonQuery();
}
context.TriggeringAction = Actions.AfterUpdate;
FireTriggers(context, list, triggers);
if(begin)
Commit();
}
catch(DeclarationException)
{
throw;
}
catch(TriggerException)
{
if(begin)
Rollback();
throw;
}
catch(DbException)
{
if(begin)
Rollback();
throw;
}
catch(Exception e)
{
if(begin)
Rollback();
throw new LightException(e.Message, e);
}
finally
{
if(dbcmd != null)
dbcmd.Dispose();
}
return result;
}
/// <summary>
/// Updates objects in the given array as objects of the generic type.
/// </summary>
/// <typeparam name="T">type that objects in the array should be treated as</typeparam>
/// <param name="list">list of objects to update</param>
/// <returns>number of records affected</returns>
public int Update<T>(ICollection list)
{
return Update(typeof(T), list);
}
/// <summary>
/// Updates given object as object of given type.
/// </summary>
/// <param name="type">data type</param>
/// <param name="obj">object to update</param>
/// <returns>number of records affected</returns>
public int Update(Type type, object obj)
{
if(obj == null)
return 0;
return Update(type, new object[] { obj });
}
/// <summary>
/// Updates the given object.
/// </summary>
/// <param name="obj">object to update</param>
/// <returns>number of records affected</returns>
public int Update(object obj)
{
if(obj == null)
return 0;
return Update(obj.GetType(), new object[] { obj });
}
/// <summary>
/// Updates the given object.
/// </summary>
/// <typeparam name="T">type that given object should be treated as</typeparam>
/// <param name="obj">object to update</param>
/// <returns>number of records affected</returns>
public int Update<T>(object obj)
{
return Update(typeof(T), obj);
}
#endregion
#region - Delete -
/// <summary>
/// Deletes object in the given array as objects of given type.
/// </summary>
/// <param name="type">data type</param>
/// <param name="list">list of objects to delete</param>
/// <returns>number of records affected</returns>
public int Delete(Type type, ICollection list)
{
if(list == null || list.Count == 0)
return 0;
if(type == null)
throw new LightException("Cannot delete objects of NULL type.");
Table table = null;
Command cmd = null;
IDbCommand dbcmd = null;
Trigger[] triggers = null;
TriggerContext context = null;
int result = 0;
bool begin = !InTransaction;
try
{
table = GetTable(type);
if(table.ReadOnly)
throw new DeclarationException("Table " + table.Name + " is marked readonly.");
if(table.KeyColumns.Length == 0)
throw new DeclarationException("Table " + table.Name + " must have at least one primary key column.");
triggers = table.Triggers;
context = new TriggerContext(this);
if(begin)
Begin();
context.TriggeringAction = Actions.BeforeDelete;
FireTriggers(context, list, triggers);
CachedCommands cc = (CachedCommands) table.CachedObject;
if(cc.Delete == null)
cc.Delete = GetDeleteCommand(table);
cmd = cc.Delete;
dbcmd = CreateDbCommand(cmd);
dbcmd.Transaction = trans;
dbcmd.Prepare();
foreach(object target in list)
{
if(target == null)
continue;
AssignParams(dbcmd, table, cmd, target);
result += dbcmd.ExecuteNonQuery();
}
context.TriggeringAction = Actions.AfterDelete;
FireTriggers(context, list, triggers);
if(begin)
Commit();
}
catch(DeclarationException)
{
throw;
}
catch(TriggerException)
{
if(begin)
Rollback();
throw;
}
catch(DbException)
{
if(begin)
Rollback();
throw;
}
catch(Exception e)
{
if(begin)
Rollback();
throw new LightException(e.Message, e);
}
finally
{
if(dbcmd != null)
dbcmd.Dispose();
}
return result;
}
/// <summary>
/// Deletes objects of given type using the given query.
/// </summary>
/// <param name="type">data type</param>
/// <param name="query">query that limits the records to be deleted</param>
/// <returns>number of records affected</returns>
public int Delete(Type type, Query query)
{
if(type == null)
throw new LightException("Cannot delete objects of NULL type.");
Table table = null;
Command cmd = null;
IDbCommand dbcmd = null;
int result = 0;
bool begin = !InTransaction;
try
{
table = GetTable(type);
if(table.ReadOnly)
throw new DeclarationException("Table " + table.Name + " is marked readonly.");
cmd = GetDeleteCommand(table, query);
dbcmd = CreateDbCommand(cmd);
if(begin)
Begin();
dbcmd.Transaction = trans;
dbcmd.Prepare();
result = dbcmd.ExecuteNonQuery();
if(begin)
Commit();
}
catch(DeclarationException)
{
throw;
}
catch(DbException)
{
if(begin)
Rollback();
throw;
}
catch(Exception e)
{
if(begin)
Rollback();
throw new LightException(e.Message, e);
}
finally
{
if(dbcmd != null)
dbcmd.Dispose();
}
return result;
}
/// <summary>
/// Deletes objects in the given array.
/// </summary>
/// <typeparam name="T">type that objects in the array should be treated as</typeparam>
/// <param name="list">list of objects to delete</param>
/// <returns>number of records affected</returns>
public int Delete<T>(ICollection list)
{
return Delete(typeof(T), list);
}
/// <summary>
/// Deletes the given object as object of given type.
/// </summary>
/// <param name="type">data type</param>
/// <param name="obj">object to delete</param>
/// <returns>number of records affected</returns>
public int Delete(Type type, object obj)
{
if(obj == null)
return 0;
return Delete(type, new object[] { obj });
}
/// <summary>
/// Deletes the given object.
/// </summary>
/// <param name="obj">object to delete</param>
/// <returns>number of records affected</returns>
public int Delete(object obj)
{
if(obj == null)
return 0;
return Delete(obj.GetType(), new object[] { obj });
}
/// <summary>
/// Deletes the given object as object of the generic type.
/// </summary>
/// <typeparam name="T">type that given object should be treated as</typeparam>
/// <param name="obj">object to delete</param>
/// <returns>number of records affected</returns>
public int Delete<T>(object obj)
{
if(obj == null)
return 0;
return Delete(typeof(T), obj);
}
/// <summary>
/// Deletes objects of type given by the generic parameter that match the given query.
/// </summary>
/// <typeparam name="T">type of objects that should be deleted</typeparam>
/// <param name="query">query that specifies records to be deleted</param>
/// <returns>number of records affected</returns>
public int Delete<T>(Query query)
{
return Delete(typeof(T), query);
}
#endregion
#region - Select -
/// <summary>
/// Returns a list of objects of given type that match the given query.
/// </summary>
/// <param name="type">type of objects to return</param>
/// <param name="query">query specifying which objects should be included in the returned array</param>
/// <param name="list">reference to the list to which to add selected objects</param>
private void Select(Type type, Query query, IList list)
{
if(type == null)
throw new LightException("Cannot select objects of NULL type.");
AbstractColumn[] columns = null;
IDataReader reader = null;
Table table = null;
Command cmd = null;
IDbCommand dbcmd = null;
try
{
table = GetTable(type);
columns = table.Columns;
cmd = GetSelectCommand(table, query);
dbcmd = CreateDbCommand(cmd);
if(InTransaction)
dbcmd.Transaction = trans;
dbcmd.Prepare();
reader = dbcmd.ExecuteReader();
while(reader.Read())
{
int index = 0;
object target = Activator.CreateInstance(type, true);
foreach(AbstractColumn column in columns)
{
if(column.Writeable)
{
object val = reader[index];
if(DBNull.Value.Equals(val))
column.Write(target, null);
else
column.Write(target, val);
}
index++;
}
list.Add(target);
}
}
catch(DeclarationException)
{
throw;
}
catch(DbException)
{
throw;
}
catch(Exception e)
{
throw new LightException(e.Message, e);
}
finally
{
if(reader != null && !reader.IsClosed)
reader.Close();
if(dbcmd != null)
dbcmd.Dispose();
}
TriggerContext context = new TriggerContext(this);
context.TriggeringAction = Actions.AfterConstruct;
try
{
FireTriggers(context, list, table.Triggers);
}
catch(TriggerException)
{
throw;
}
catch(Exception e)
{
throw new LightException(e.Message, e);
}
}
/// <summary>
/// Returns a list of objects of given type that match the given query.
/// Returned IList can safely be cast to ArrayList.
/// </summary>
/// <param name="type">type of objects to be returned</param>
/// <param name="query">query that specifies which objects should be included in the returned list</param>
/// <returns>list of objects of given type that matched given query</returns>
public IList Select(Type type, Query query)
{
ArrayList list = new ArrayList();
Select(type, query, list);
return list;
}
/// <summary>
/// Returns a list of all objects of given type.
/// Returned IList can safely be cast to ArrayList.
/// </summary>
/// <param name="type">type of objects to be returned</param>
/// <returns>list of objects of given type</returns>
public IList Select(Type type)
{
return Select(type, null);
}
/// <summary>
/// Returns a list of objects of type given by the generic parameter that match the given query.
/// Returned IList can safely be cast to List<T>.
/// </summary>
/// <typeparam name="T">type of objects that should be returned</typeparam>
/// <param name="query">query that specifies which objects should be included in the returned list</param>
/// <returns>list of objects of generic type that match the given query</returns>
public IList<T> Select<T>(Query query)
{
List<T> list = new List<T>();
Select(typeof(T), query, list);
return list;
}
/// <summary>
/// Returns a list of all objects of type given by the generic parameter.
/// Returned IList can safely be cast to List<T>.
/// </summary>
/// <typeparam name="T">type of objects that should be returned</typeparam>
/// <returns>list of objects of generic type</returns>
public IList<T> Select<T>()
{
return Select<T>(null);
}
#endregion
#region - Find -
/// <summary>
/// Returns an object of given type with the given primary key value.
/// For this method to work properly, the given type should only define a single
/// primary key column. If more then 1 object is returned for the given
/// key, only the first one will be returned by this method.
/// </summary>
/// <param name="type">type of object to return</param>
/// <param name="key">value of the primary key column</param>
/// <returns>object of the given type with given primary key value</returns>
public object Find(Type type, object key)
{
if(key == null)
return null;
if(type == null)
throw new LightException("Cannot find an object of NULL type.");
Table table = GetTable(type);
if(table.KeyColumns.Length != 1)
throw new DeclarationException("Type " + type.Name + " must have exactly one primary key column to be used with Dao.Find method.");
CachedCommands cc = (CachedCommands) table.CachedObject;
if(cc.Find == null)
cc.Find = GetFindQuery(table);
Query query = cc.Find;
query.Parameters[0].Value = key;
IList list = Select(type, query);
if(list.Count > 0)
return list[0];
return null;
}
/// <summary>
/// Returns an object of the generic type with the given primary key value.
/// For this method to work properly, the generic type should only define a single
/// primary key column. If more then 1 object is returned for the given
/// key, only the first one will be returned by this method.
/// </summary>
/// <typeparam name="T">type of object to return</typeparam>
/// <param name="key">value of the primary key column</param>
/// <returns>object of generic type with given primary key value</returns>
public T Find<T>(object key)
{
Type type = typeof(T);
object result = Find(type, key);
if(result == null)
return default(T);
return (T) result;
}
#endregion
#region - Call -
/// <summary>
/// Calls a given stored procedure and creates objects of given type
/// from the resulting data reader. Created objects are added to the list.
/// </summary>
/// <param name="type">types of objects to create</param>
/// <param name="proc">procedure to call</param>
/// <param name="list">list to which to add created objects</param>
private void Call(Type type, Procedure proc, IList list)
{
if(type == null)
throw new LightException("Cannot call procedures with null type.");
Table table = null;
AbstractColumn[] columns = null;
Command cmd = null;
IDbCommand dbcmd = null;
IDataReader reader = null;
try
{
table = GetTable(type);
cmd = GetProcedureCommand(proc);
dbcmd = CreateDbCommand(cmd);
columns = table.Columns;
if(InTransaction)
dbcmd.Transaction = trans;
dbcmd.Prepare();
reader = dbcmd.ExecuteReader();
while(reader.Read())
{
object target = Activator.CreateInstance(type, true);
foreach(AbstractColumn column in columns)
{
if(column.Writeable)
{
int index = reader.GetOrdinal(column.Name);
if(index > -1)
{
object val = reader.GetValue(index);
if(DBNull.Value.Equals(val))
column.Write(target, null);
else
column.Write(target, val);
}
}
}
list.Add(target);
}
}
catch(DeclarationException) {
throw;
}
catch(DbException) {
throw;
}
catch(Exception e) {
throw new LightException(e.Message, e);
}
finally {
if(reader != null && !reader.IsClosed)
reader.Close();
if(dbcmd != null)
dbcmd.Dispose();
}
TriggerContext context = new TriggerContext(this);
context.TriggeringAction = Actions.AfterConstruct;
try {
FireTriggers(context, list, table.Triggers);
}
catch(TriggerException) {
throw;
}
catch(Exception e) {
throw new LightException(e.Message, e);
}
}
/// <summary>
/// Returns a list of objects of given type from calling given procedure.
/// </summary>
/// <param name="type">type of objects to return</param>
/// <param name="procedure">procedure to call</param>
/// <returns>list of objects from calling given procedure</returns>
public IList Call(Type type, Procedure procedure)
{
IList list = new ArrayList();
Call(type, procedure, list);
return list;
}
/// <summary>
/// Returns a list of objects of generic type from calling given procedure.
/// </summary>
/// <param name="procedure">procedure to call</param>
/// <returns>list of objects from calling given procedure</returns>
public IList<T> Call<T>(Procedure procedure)
{
IList<T> list = new List<T>();
Call(typeof(T), procedure, (IList)list);
return list;
}
#endregion
#region - Helpers -
/// <summary>
/// Fires all given triggers in the given context for given target objects.
/// </summary>
/// <param name="context">trigger context</param>
/// <param name="targets">list of target objects</param>
/// <param name="triggers">triggers to be fired</param>
private void FireTriggers(TriggerContext context, ICollection targets, Trigger[] triggers)
{
if(triggers.Length == 0)
return;
List<Trigger> list = new List<Trigger>();
int action = (int)context.TriggeringAction;
foreach(Trigger trigger in triggers)
{
if( (action & (int)trigger.Action) == action )
list.Add(trigger);
}
try
{
foreach(object target in targets)
{
foreach(Trigger trigger in list)
{
trigger.Method.Invoke(target, context.AsObjectArray);
if(context.Failed)
throw new TriggerException(context.Message);
}
}
}
catch(TriggerException)
{
throw;
}
catch(Exception e)
{
throw new TriggerException(e.Message, e);
}
}
/// <summary>
/// Creates an IDbCommand from given command object.
/// </summary>
/// <param name="c">source command object</param>
/// <returns>an instance of IDbCommand assiciated with current connection</returns>
private IDbCommand CreateDbCommand(Command c)
{
IDbCommand cmd = cn.CreateCommand();
cmd.CommandText = c.Text;
cmd.CommandType = CommandType.Text;
int count = c.Count;
for(int i = 0; i < count; i++)
{
Parameter p = c.Get(i);
IDbDataParameter p1 = cmd.CreateParameter();
p1.ParameterName = p.ParameterName;
p1.DbType = p.DBType;
p1.Direction = p.Direction;
p1.Precision = p.Precision;
p1.Scale = p.Scale;
p1.Size = p.Size;
p1.Value = p.Value ?? DBNull.Value;
cmd.Parameters.Add(p1);
}
return cmd;
}
/// <summary>
/// Assigns parameter values to db command from target object.
/// </summary>
/// <param name="dbcmd">db command</param>
/// <param name="table">table model</param>
/// <param name="cmd">command model</param>
/// <param name="target">target object</param>
private void AssignParams(IDbCommand dbcmd, Table table, Command cmd, object target)
{
int count = cmd.Count;
for(int i = 0; i < count; i++)
{
Parameter p = cmd.Get(i);
IDbDataParameter p2 = (IDbDataParameter) dbcmd.Parameters[i];
if(!string.IsNullOrEmpty(p.ColumnName))
{
AbstractColumn column = table.GetColumn(p.ColumnName);
if(column != null)
{
object val = column.Read(target);
if(val == null)
p2.Value = DBNull.Value;
else
p2.Value = val;
}
}
}
}
#endregion
#region - Abstract methods -
/// <summary>
/// Creates an insert command for given table.
/// </summary>
/// <param name="table">table for which to create an insert command</param>
/// <returns>insert command</returns>
protected abstract Command GetInsertCommand(Table table);
/// <summary>
/// Returns the next value in a sequence for given table.
/// If sequence if not defined or sequences are not used this method should return null.
/// This method is called before an object is inserted into a given table. The returned
/// value will be assigned to the auto-incremented column defined on a table.
/// </summary>
/// <param name="table">table into which the target object will be inserted</param>
/// <returns>next value in a sequence for given table, or null if there is not sequence</returns>
protected abstract object NextSequenceValue(Table table);
/// <summary>
/// Returns the last auto incremented value for given table.
/// If there are no auto incremented values, this method should return null.
/// If a non-null value is returned, it will be assigned to the auto increment
/// column of the last inserted object.
/// </summary>
/// <param name="table">table into which a record was inserted</param>
/// <param name="command">actual database command used to insert a new record</param>
/// <returns>auto incremented value</returns>
protected abstract object AutoIncrementedValue(Table table, IDbCommand command);
/// <summary>
/// Creates an update command for given table.
/// </summary>
/// <param name="table">table for which to create an update command</param>
/// <returns>update command</returns>
protected abstract Command GetUpdateCommand(Table table);
/// <summary>
/// Creates a delete command for given table to delete 1 record.
/// </summary>
/// <param name="table">table for which to create a delete command</param>
/// <returns>delete command</returns>
protected abstract Command GetDeleteCommand(Table table);
/// <summary>
/// Creates a delete command for given table using given query.
/// NOTE: The query object may be null.
/// </summary>
/// <param name="table">table for which to create a delete command</param>
/// <param name="query">query that identifies records to delete</param>
/// <returns>delete command</returns>
protected abstract Command GetDeleteCommand(Table table, Query query);
/// <summary>
/// Creates a select command for given table using given query.
/// NOTE: The query object may be null.
/// </summary>
/// <param name="table">table for which to create a select command</param>
/// <param name="query">query that identifies records to select</param>
/// <returns></returns>
protected abstract Command GetSelectCommand(Table table, Query query);
/// <summary>
/// Creates a query that would select a single row based on the
/// primary key column of the given table. The query must have at least
/// one (and usually the only) parameter whose value will be set to the
/// searched key.
/// </summary>
/// <param name="table">target table</param>
/// <returns>query that would select a single row</returns>
protected abstract Query GetFindQuery(Table table);
/// <summary>
/// Creates a command for calling a given procedure.
/// </summary>
/// <param name="procedure">procedure to call</param>
/// <returns>command that calls given procedure</returns>
protected abstract Command GetProcedureCommand(Procedure procedure);
#endregion
#region - Private class -
/// <summary>
/// Cached commands for a table model.
/// </summary>
private class CachedCommands
{
/// <summary>
/// Insert command.
/// </summary>
public Command Insert;
/// <summary>
/// Update command.
/// </summary>
public Command Update;
/// <summary>
/// Delete command.
/// </summary>
public Command Delete;
/// <summary>
/// Find query.
/// </summary>
public Query Find;
}
#endregion
}
}