Click here to Skip to main content
15,893,588 members
Articles / Database Development / SQL Server

Light ORM Library for .NET

Rate me:
Please Sign up or sign in to vote.
4.83/5 (39 votes)
8 Oct 2010CPOL17 min read 222.5K   3.1K   184  
This article is about the Light Object-Relational Mapping library.
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&lt;T&gt;.
		/// </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&lt;T&gt;.
		/// </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
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions