Click here to Skip to main content
15,881,812 members
Articles / Programming Languages / SQL

DataLoaders - Unified Data to Object Binding

Rate me:
Please Sign up or sign in to vote.
4.72/5 (35 votes)
31 Oct 2004CPOL19 min read 76.2K   2.9K   83  
A framework to completely separate objects from their data source - allowing any data source to be used without prior consideration in your code or designs. Databases, text files, web services and potentially anything else can all be used or swapped transparently.
using System;
using System.Data;
using System.Data.OleDb;
using System.Xml;
using System.Collections;

namespace Bttlxe.Data
{
	/// <summary>
	/// Data loader implementing <see cref="IDataLoader"/> to access data in a MySQL database.
	/// <seealso cref="IDataLoader"/>
	/// </summary>
	/// <remarks>
	/// Uses SQL as the query language.
	/// </remarks>
	/// <example>
	/// <p>The example below demonstrates using a <see cref="MySqlDataLoader"/> to load data into a <see cref="Bttlxe.Controls.NewsList"/>
	/// collection which implements <see cref="IDataItem"/>.  (Note that the definition of the <see cref="DataItemDictionary"/> is
	/// only needed where name transformation needs to occur, but is shown here for completeness.)</p>
	/// <p class="lang">[C#]</p>
	/// <code>
	/// Bttlxe.Controls.NewsList oNewsList = new Bttlxe.Controls.NewsList();
	/// 
	/// MySqlDataLoader oLoader = new MySqlDataLoader();
	/// oLoader.Server = "(local)";
	/// oLoader.Database = "tempdb";
	/// 
	/// DataItemDictionary keys = new DataItemDictionary();
	/// keys.Add("ID", "n_id");
	/// keys.Add("Title", "n_title");
	/// keys.Add("Description", "n_description");
	/// keys.Add("Owner", "n_owner");
	/// keys.Add("Created", "n_created");
	/// 
	/// IDataItem oItem = (IDataItem)oNewsList;
	///	oLoader.Execute(ref oItem, ref keys, DataOperation.Read);
	/// </code>
	/// <p>The example below accomplishes the same result as above but without using an <see cref="IDataItem"/> object.</p>
	/// <p class="lang">[C#]</p>
	/// <code>
	/// MySqlDataLoader oLoader = new MySqlDataLoader();
	/// oLoader.Server = "(local)";
	/// oLoader.Database = "tempdb";
	/// 
	/// Bttlxe.Controls.NewsList oNewsList = new Bttlxe.Controls.NewsList();
	/// oNewsList.DataSource = oLoader.ExecuteDataTable("SELECT * FROM [News]");
	/// oNewsList.DataBind();
	/// </code>
	/// <p>The example below demonstrates using a <see cref="DataItem"/> object to write data to your data source.</p>
	/// <p class="lang">[C#]</p>
	/// <code>
	/// MySqlDataLoader oLoader = new MySqlDataLoader();
	/// oLoader.Server = "(local)";
	/// oLoader.Database = "tempdb";
	/// 
	/// DataTable dt = new DataTable("News");
	/// dt.Columns.Add("ID", System.Type.GetType("System.Int32"));
	/// dt.Columns.Add("Title", System.Type.GetType("System.String"));
	/// dt.Columns.Add("Description", System.Type.GetType("System.String"));
	/// dt.Columns.Add("Owner", System.Type.GetType("System.String"));
	/// dt.Columns.Add("Created", System.Type.GetType("System.DateTime"));
	/// 
	/// dt.Columns["ID"].AllowDBNull = false;
	/// dt.Columns["ID"].AutoIncrement = true;
	/// dt.Columns["ID"].AutoIncrementSeed = 1;
	/// dt.Columns["ID"].AutoIncrementStep = 1;
	/// dt.Columns["ID"].ReadOnly = true;
	/// dt.PrimaryKey = new DataColumn[]{dt.Columns["ID"]};
	/// 
	/// dt.Columns["Title"].AllowDBNull = false;
	/// dt.Columns["Title"].MaxLength = 50;
	/// dt.Columns["Owner"].AllowDBNull = false;
	/// dt.Columns["Owner"].MaxLength = 50;
	/// dt.Columns["Owner"].DefaultValue = "Anonymous";
	/// dt.Columns["Created"].AllowDBNull = false;
	/// 
	/// DataItemDictionary keys = new DataItemDictionary();
	/// keys.Add("ID", "n_id");
	/// keys.Add("Title", "n_title");
	/// keys.Add("Description", "n_description");
	/// keys.Add("Owner", "n_owner");
	/// keys.Add("Created", "n_created");
	/// 
	/// DataItem oDataItem = new DataItem();
	/// oDataItem.Data = dt;
	/// oDataItem.Keys = keys;
	/// 
	/// // perform some operation that adds data to the DataItem.
	/// ...
	/// 
	/// oLoader.Execute(ref oDataItem, DataOperation.Write);
	/// </code>
	/// </example>
	public class MySqlDataLoader : IDataLoader, IDisposable
	{
		#region Attributes

		// The database connection.
		private OleDbConnection m_oConnection = null;

		// Show trace helper information.
		private bool m_bTrace = false;

		// Keep connection alive.
		private bool m_bKeepAlive = true;

		// The connection string for the database.
		private string m_strConnection = string.Empty;

		// The server name.
		private string m_strServer = "(local)";

		// The database name.
		private string m_strDatabase = "tempdb";

		// The User ID field.
		private string m_strUserID = "admin";

		// The Password field.
		private string m_strPassword = "";

		#endregion

		#region Properties

		/// <summary>
		/// The database connection.
		/// </summary>
		/// <value>
		/// An <see cref="OleDbConnection"/> for the database.
		/// </value>
		public OleDbConnection Connection
		{
			get
			{
				OpenConnection();

				return m_oConnection;
			}
		}

		/// <summary>
		/// The server name.
		/// </summary>
		/// <remarks>
		/// <p>If you want To connect with MySql running on the same computer, you must to specify the keyword <c>(local)</c>.</p>
		/// </remarks>
		public string Server
		{
			get
			{
				return m_strServer;
			}
			set
			{
				m_strServer = value;
			}
		}

		/// <summary>
		/// The database name.
		/// </summary>
		public string Database
		{
			get
			{
				return m_strDatabase;
			}
			set
			{
				m_strDatabase = value;
			}
		}

		/// <summary>
		/// The User ID field.
		/// </summary>
		public string UserID
		{
			get
			{
				return m_strUserID;
			}
			set
			{
				m_strUserID = value;
			}
		}

		/// <summary>
		/// The Password field.
		/// </summary>
		public string Password
		{
			get
			{
				return m_strPassword;
			}
			set
			{
				m_strPassword = value;
			}
		}

		/// <summary>
		/// Show trace helper information.
		/// </summary>
		/// <value>
		/// Whether the data loader will output helper information to the application's trace log.
		/// </value>
		public bool Trace
		{
			get
			{
				return m_bTrace;
			}
			set
			{
				m_bTrace = value;
			}
		}

		/// <summary>
		/// Keep connection alive.
		/// </summary>
		/// <remarks>
		/// It is not recommended that you change this value once the data loader has been initialised as
		/// results may be unpredictable.
		/// </remarks>
		/// <value>
		/// Whether the data loader will manage the database connection and ensure it is always open.
		/// </value>
		public bool KeepAlive
		{
			get
			{
				return m_bKeepAlive;
			}
			set
			{
				m_bKeepAlive = value;
			}
		}

		/// <summary>
		/// The connection string for the database.
		/// </summary>
		/// <remarks>
		/// You should not include the Provider attribute in the connection string.
		/// </remarks>
		public string ConnectionString
		{
			get
			{
				if (m_strDatabase.Length > 0 && m_strServer.Length > 0)
				{
					string strConnection = "Provider=MySQLProv; DB=" + m_strDatabase + "; Server=" + m_strServer + ";";
					strConnection += " User ID=" + m_strUserID + "; Password=" + m_strPassword + ";";

					return strConnection;
				}

				return m_strConnection;
			}
			set
			{
				m_strConnection = value;
			}
		}

		#endregion

		/// <summary>
		/// Create the data loader.
		/// </summary>
		public MySqlDataLoader()
		{
		}

		/// <summary>
		/// Create the data loader.
		/// </summary>
		/// <param name="bKeepAlive">Keep connection alive.</param>
		public MySqlDataLoader(bool bKeepAlive)
		{
			m_bKeepAlive = bKeepAlive;
		}

		/// <summary>
		/// Create the data loader.
		/// </summary>
		/// <param name="bKeepAlive">Keep connection alive.</param>
		/// <param name="bTrace">Show trace helper information.</param>
		public MySqlDataLoader(bool bKeepAlive, bool bTrace)
		{
			m_bKeepAlive = bKeepAlive;
			m_bTrace = bTrace;
		}

		/// <summary>
		/// Opens the database connection.
		/// </summary>
		/// <remarks>
		/// Used internally.
		/// </remarks>
		protected void OpenConnection()
		{
			if (m_bTrace)
				DoTrace("MySqlDataLoader.OpenConnection", "Opening database connection.\n" + ConnectionString);

			if (m_oConnection == null || m_oConnection.State == ConnectionState.Closed)
			{
				m_oConnection = new OleDbConnection(ConnectionString);
				m_oConnection.Open();
			}
		}

		/// <summary>
		/// Closes the database connection
		/// </summary>
		/// <remarks>
		/// Used internally.
		/// </remarks>
		protected void CloseConnection()
		{
			if (m_bTrace)
				DoTrace("MySqlDataLoader.CloseConnection", "Closing database connection");

			if (m_oConnection != null)
			{
				m_oConnection.Close();
				m_oConnection = null;
			}
		}

		#region IDataLoader Members

		/// <summary>
		/// Initialise the data loader.
		/// </summary>
		/// <remarks>
		/// Initialises the connection that is present for the lifetime of the data loader if
		/// <see cref="KeepAlive"/> is true.
		/// </remarks>
		public void Initialise()
		{
			if (m_bKeepAlive)
				OpenConnection();
		}

		/// <summary>
		/// Terminate the data loader.
		/// </summary>
		/// <remarks>
		/// Terminates the connection that is present for the lifetime of the data loader if
		/// <see cref="KeepAlive"/> is true.
		/// This method should not be called directly, use the <see cref="Dispose"/> method instead.
		/// </remarks>
		public void Terminate()
		{
			if (m_bKeepAlive)
				CloseConnection();
		}

		/// <summary>
		/// Executes a scalar SELECT query.
		/// </summary>
		/// <param name="strQuery">The SQL query to evaluate.</param>
		/// <returns>The first record or the first row returned.</returns>
		public object ExecuteScaler(string strQuery)
		{
			DataItem dataItem = new DataItem();
			DataItemDictionary keys = new DataItemDictionary();
			string strTableName = GetTableFromQuery(strQuery);

			// get the mapping
			if (NameMappingCallback != null)
				NameMappingCallback(strTableName, ref keys);

			if (keys.Count > 0)
			{
				// translate names
				foreach (DictionaryEntry oItem in keys)
					strQuery = strQuery.Replace("[" + (string)oItem.Key + "]", "[" + (string)oItem.Value + "]");
			}

			if (m_bTrace)
				DoTrace("MySqlDataLoader.ExecuteScalar", strQuery);

			object oRetVal = null;

			// two scenarios - if the connection is already open use it, else open our
			// .. own connection, query it, then close it again
			if (m_oConnection != null)
			{
				OleDbCommand cmd = new OleDbCommand(strQuery, m_oConnection);
				oRetVal = cmd.ExecuteScalar();
			}
			else
			{
				OleDbConnection oConn = new OleDbConnection(ConnectionString);
				oConn.Open();
				OleDbCommand cmd = new OleDbCommand(strQuery, oConn);
				oRetVal = cmd.ExecuteScalar();
				oConn.Close();
			}

			return oRetVal;
		}

		/// <summary>
		/// Executes a SELECT query.
		/// </summary>
		/// <param name="strQuery">The SQL query to evaluate.</param>
		/// <returns>A <see cref="DataTable"/> containing any result.</returns>
		public DataTable ExecuteDataTable(string strQuery)
		{
			DataItem dataItem = new DataItem();
			DataItemDictionary keys = new DataItemDictionary();
			string strTableName = GetTableFromQuery(strQuery);

			// get the mapping
			if (NameMappingCallback != null)
				NameMappingCallback(strTableName, ref keys);

			if (keys.Count > 0)
			{
				// translate names
				foreach (DictionaryEntry oItem in keys)
					strQuery = strQuery.Replace("[" + (string)oItem.Key + "]", "[" + (string)oItem.Value + "]");
			}

			if (m_bTrace)
				DoTrace("MySqlDataLoader.ExecuteDataTable", strQuery);

			if (m_oConnection == null || m_oConnection.State == ConnectionState.Closed)
				OpenConnection();

			OleDbDataAdapter oAdaptor = new OleDbDataAdapter();
			oAdaptor.SelectCommand = new OleDbCommand(strQuery, m_oConnection);

			DataSet oDataSet = new DataSet();
			oAdaptor.Fill(oDataSet);
			oDataSet.Tables[0].TableName = strTableName;

			if (keys.Count > 0)
			{
				// revert back to our proxy names
				DataItemDictionary values = new DataItemDictionary();

				foreach (DictionaryEntry oItem in keys)
					values.Add((string)oItem.Value, (string)oItem.Key);

				foreach (DataColumn oColumn in oDataSet.Tables[0].Columns)
					oDataSet.Tables[0].Columns[oColumn.ColumnName].ColumnName = values[oColumn.ColumnName];
			}

			return oDataSet.Tables[0];
		}

		/// <summary>
		/// Executes a SELECT query.
		/// </summary>
		/// <param name="strQuery">The SQL query to evaluate.</param>
		/// <returns>A <see cref="IDataReader"/> containing any result.</returns>
		public IDataReader ExecuteReader(string strQuery)
		{
			if (m_bTrace)
				DoTrace("MySqlDataLoader.ExecuteReader", strQuery);

			OleDbDataReader oReader = null;

			if (m_oConnection == null || m_oConnection.State == ConnectionState.Closed)
				OpenConnection();

			OleDbCommand cmd = new OleDbCommand(strQuery, m_oConnection);
			oReader = cmd.ExecuteReader();

			return oReader;
		}

		/// <summary>
		/// <b>[Unsupported by this data loader].</b>
		/// </summary>
		/// <param name="strQuery">The SQL query to evaluate.</param>
		/// <returns>Null.</returns>
		/// <remarks>
		/// This method is unsupported by this data loader.  Use <see cref="ExecuteReader"/> instead.
		/// </remarks>
		public XmlReader ExecuteXmlReader(string strQuery)
		{
			return null;
		}

		/// <summary>
		/// Executes a non query (UPDATE, INSERT, or DELETE).
		/// </summary>
		/// <param name="strQuery">The SQL query to evaluate.</param>
		/// <returns>The number of records affected.</returns>
		public int ExecuteNonQuery(string strQuery)
		{
			DataItem dataItem = new DataItem();
			DataItemDictionary keys = new DataItemDictionary();
			string strTableName = GetTableFromQuery(strQuery);

			// get the mapping
			if (NameMappingCallback != null)
				NameMappingCallback(strTableName, ref keys);

			if (keys.Count > 0)
			{
				// translate names
				foreach (DictionaryEntry oItem in keys)
					strQuery = strQuery.Replace("[" + (string)oItem.Key + "]", "[" + (string)oItem.Value + "]");
			}

			if (m_bTrace)
				DoTrace("MySqlDataLoader.ExecuteNonQuery", strQuery);

			int nRetVal = 0;

			// two scenarios - if the connection is already open use it, else open our
			// .. own connection, query it, then close it again
			if (m_oConnection != null)
			{
				OleDbCommand cmd = new OleDbCommand(strQuery, m_oConnection);
				nRetVal = cmd.ExecuteNonQuery();
			}
			else
			{
				OleDbConnection oConn = new OleDbConnection(ConnectionString);
				oConn.Open();
				OleDbCommand cmd = new OleDbCommand(strQuery, oConn);
				nRetVal = cmd.ExecuteNonQuery();
				oConn.Close();
			}

			return nRetVal;
		}

		/// <summary>
		/// Executes a SELECT query.
		/// </summary>
		/// <param name="strQuery">The SQL query to evaluate.</param>
		/// <returns>A <see cref="IDataItem"/> containing any result.</returns>
		public IDataItem Execute(string strQuery)
		{
			DataItem dataItem = new DataItem();
			DataItemDictionary keys = new DataItemDictionary();
			string strTableName = GetTableFromQuery(strQuery);

			// get the mapping
			if (NameMappingCallback != null)
				NameMappingCallback(strTableName, ref keys);

			// translate names
			foreach (DictionaryEntry oItem in keys)
				strQuery = strQuery.Replace("[" + (string)oItem.Key + "]", "[" + (string)oItem.Value + "]");

			// execute
			if (m_bTrace)
				DoTrace("MySqlDataLoader.Execute", strQuery);

			DataSet oDataSet = new DataSet();

			// two scenarios - if the connection is already open use it, else open our
			// .. own connection, query it, then close it again
			if (m_oConnection != null)
			{
				OleDbDataAdapter oAdaptor = new OleDbDataAdapter();
				oAdaptor.SelectCommand = new OleDbCommand(strQuery, m_oConnection);
				oAdaptor.Fill(oDataSet);

				oDataSet.Tables[0].TableName = strTableName;

				// revert back to our proxy names
				if (keys != null && keys.Count > 0)
				{
					DataItemDictionary values = new DataItemDictionary();

					foreach (DictionaryEntry oItem in keys)
						values.Add((string)oItem.Value, (string)oItem.Key);

					foreach (DataColumn oColumn in oDataSet.Tables[0].Columns)
						oDataSet.Tables[0].Columns[oColumn.ColumnName].ColumnName = values[oColumn.ColumnName];
				}
			}
			else
			{
				OleDbConnection oConn = new OleDbConnection(ConnectionString);
				oConn.Open();

				OleDbDataAdapter oAdaptor = new OleDbDataAdapter();
				oAdaptor.SelectCommand = new OleDbCommand(strQuery, oConn);

				oAdaptor.Fill(oDataSet);

				oDataSet.Tables[0].TableName = strTableName;

				// revert back to our proxy names
				if (keys != null && keys.Count > 0)
				{
					DataItemDictionary values = new DataItemDictionary();

					foreach (DictionaryEntry oItem in keys)
						values.Add((string)oItem.Value, (string)oItem.Key);

					foreach (DataColumn oColumn in oDataSet.Tables[0].Columns)
						oDataSet.Tables[0].Columns[oColumn.ColumnName].ColumnName = values[oColumn.ColumnName];
				}

				oConn.Close();
			}

			// apply the schema to the data
			/*DataTable dtNew = dataItem.Schema.Tables[0].Clone();

			foreach (DataRow oRow in oDataSet.Tables[0].Rows)
				dtNew.ImportRow(oRow);

			oDataSet.Tables.Clear();
			oDataSet.Tables.Add(dtNew);*/

			dataItem.Data = oDataSet.Copy();

			return dataItem;
		}

		/// <summary>
		/// Executes a query.
		/// </summary>
		/// <param name="dataItem">The <see cref="IDataItem"/> either containing the data passed to this method or that will receive any records returned.</param>
		/// <param name="operation">The <see cref="DataOperation"/> to perform.</param>
		public void Execute(ref IDataItem dataItem, DataOperation operation)
		{
			if (dataItem == null)
				throw(new ArgumentNullException("dataItem"));

			string strQuery = string.Empty;
			DataTable dtThis = null;

			// drop and create operations use the data item schema not data
			if (operation == DataOperation.Create || operation == DataOperation.Drop)
			{
				// if we have multiple data tables
				if (dataItem.Schema.Tables.Count > 1)
				{
					DataSet dsResults = new DataSet(dataItem.Schema.DataSetName);

					foreach (DataTable dt in dataItem.Schema.Tables)
					{
						DataSet ds = new DataSet();
						ds.Tables.Add(dt.Copy());

						DataItem di = new DataItem(ds);
						IDataItem idi = (IDataItem)di;

						Execute(ref idi, operation);

						dsResults.Tables.Add(ds.Tables[0].Copy());
					}

					return;
				}

				dtThis = dataItem.Schema.Tables[0];
			}
			else
			{
				// if we have multiple data tables
				if (dataItem.Data.Tables.Count > 1)
				{
					DataSet dsResults = new DataSet(dataItem.Data.DataSetName);

					foreach (DataTable dt in dataItem.Data.Tables)
					{
						DataSet ds = new DataSet();
						ds.Tables.Add(dt.Copy());

						DataItem di = new DataItem(ds);
						IDataItem idi = (IDataItem)di;

						Execute(ref idi, operation);

						dsResults.Tables.Add(ds.Tables[0].Copy());
					}

					dataItem.Data = dsResults;

					return;
				}

				dtThis = dataItem.Data.Tables[0];
			}

			// determine the type of query (get or set)
			if (operation == DataOperation.Read)
			{
				// SELECT
				strQuery = "SELECT ";

				foreach (DataColumn oColumn in dtThis.Columns)
					strQuery += "[" + oColumn.ColumnName + "]";

				strQuery = strQuery.Replace("][", "], [");

				// FROM
				strQuery += " FROM [" + dtThis.TableName + "]";

				// WHERE
				if (dataItem.Condition != null && dataItem.Condition.Length > 0)
					strQuery += " WHERE (" + dataItem.Condition + ")";
				else
				{
					int nReadOnlyColumnCount = 0;

					foreach (DataColumn oColumn in dtThis.Columns)
					{
						bool bPrimaryKey = false;

						foreach (DataColumn oColumnKey in dtThis.PrimaryKey)
						{
							if (oColumnKey.ColumnName == oColumn.ColumnName)
								bPrimaryKey = true;
						}

						if (oColumn.ReadOnly || bPrimaryKey)
						{
							nReadOnlyColumnCount++;
							continue;
						}
					}

					if (nReadOnlyColumnCount > 0 && dtThis.Rows.Count > 0)
					{
						strQuery += " WHERE (";
						int nColumnCount = 0;

						foreach (DataColumn oColumn in dtThis.Columns)
						{
							nColumnCount++;

							bool bPrimaryKey = false;

							foreach (DataColumn oColumnKey in dtThis.PrimaryKey)
							{
								if (oColumnKey.ColumnName == oColumn.ColumnName)
									bPrimaryKey = true;
							}

							if (oColumn.ReadOnly || bPrimaryKey)
								strQuery += "[" + oColumn.ColumnName + "]=" + GetValueFromType(oColumn.DataType, dtThis.Rows[0][oColumn.ColumnName]);

							if (nColumnCount < nReadOnlyColumnCount)
								strQuery += " AND ";
						}

						strQuery += ")";
					}
				}

				// ORDER BY
				if (dataItem.Sort.Length > 0)
					strQuery += " ORDER BY " + dataItem.Sort;

				DataSet oDataSet = new DataSet();
				oDataSet.Tables.Add(ExecuteDataTable(strQuery).Copy());

				// apply the schema to the data
				DataTable dtNew = dataItem.Schema.Tables[0].Clone();

				foreach (DataRow oRow in oDataSet.Tables[0].Rows)
					dtNew.ImportRow(oRow);

				oDataSet.Tables.Clear();
				oDataSet.Tables.Add(dtNew);

				dataItem.Data = oDataSet.Copy();
			}
			else if (operation == DataOperation.Delete)
			{
				// DELETE
				strQuery = "DELETE FROM [" + dtThis.TableName + "]";

				// now execute for each row we are inserting
				foreach (DataRow oRow in dtThis.Rows)
				{
					string strQueryThis = strQuery;
					int nColumnCount = 0;
					int nReadOnlyColumnCount = 0;

					foreach (DataColumn oColumn in dtThis.Columns)
					{
						nColumnCount++;

						bool bPrimaryKey = false;

						foreach (DataColumn oColumnKey in dtThis.PrimaryKey)
						{
							if (oColumnKey.ColumnName == oColumn.ColumnName)
								bPrimaryKey = true;
						}

						if (oColumn.ReadOnly || bPrimaryKey)
						{
							nReadOnlyColumnCount++;
							continue;
						}
					}

					// check we are updating at least one column or that at least one column is a key!
					if (nReadOnlyColumnCount == nColumnCount || nReadOnlyColumnCount == 0)
						return;

					// WHERE
					if (dataItem.Condition != null && dataItem.Condition.Length > 0)
						strQueryThis += " WHERE (" + dataItem.Condition + ")";
					else if (nReadOnlyColumnCount > 0)
					{
						strQueryThis += " WHERE (";
						nColumnCount = 0;

						foreach (DataColumn oColumn in dtThis.Columns)
						{
							nColumnCount++;

							bool bPrimaryKey = false;

							foreach (DataColumn oColumnKey in dtThis.PrimaryKey)
							{
								if (oColumnKey.ColumnName == oColumn.ColumnName)
									bPrimaryKey = true;
							}

							if (oColumn.ReadOnly || bPrimaryKey)
								strQueryThis += "[" + oColumn.ColumnName + "]=" + GetValueFromType(oColumn.DataType, oRow[oColumn.ColumnName]);

							if (nColumnCount < nReadOnlyColumnCount)
								strQueryThis += " AND ";
						}

						strQueryThis += ")";
					}

					// execute
					this.ExecuteNonQuery(strQueryThis);
				}
			}
			else if (operation == DataOperation.Drop)
			{
				// DROP TABLE
				strQuery = "DROP TABLE [" + dtThis.TableName + "]";

				// WHERE
				if (dataItem.Condition != null && dataItem.Condition.Length > 0)
					strQuery += " WHERE (" + dataItem.Condition + ")";

				// execute
				this.ExecuteNonQuery(strQuery);
			}
			else if (operation == DataOperation.Write)
			{
				// check we dont exist (based on our keys) else INSERT
				bool bHasIdentity = false;
				int nReadOnlyColumnCount = 0;

				foreach (DataColumn oColumn in dtThis.Columns)
				{
					bool bPrimaryKey = false;

					foreach (DataColumn oColumnKey in dtThis.PrimaryKey)
					{
						if (oColumnKey.ColumnName == oColumn.ColumnName)
							bPrimaryKey = true;
					}

					if (oColumn.AutoIncrement)
						bHasIdentity = true;

					if (oColumn.ReadOnly || bPrimaryKey)
					{
						nReadOnlyColumnCount++;
						continue;
					}
				}

				strQuery = "INSERT INTO [" + dtThis.TableName + "] (";

				foreach (DataColumn oColumn in dtThis.Columns)
				{
					bool bPrimaryKey = false;

					foreach (DataColumn oColumnKey in dtThis.PrimaryKey)
					{
						if (oColumnKey.ColumnName == oColumn.ColumnName)
							bPrimaryKey = true;
					}

					//if (oColumn.ReadOnly)
					//	continue;

					strQuery += "[" + oColumn.ColumnName + "]";
				}

				strQuery = strQuery.Replace("][", "], [");

				strQuery += ") VALUES (@VALUES@)";

				// WHERE
				if (dataItem.Condition != null && dataItem.Condition.Length > 0)
					strQuery += " WHERE (" + dataItem.Condition + ")";

				// now execute for each row we are inserting
				foreach (DataRow oRow in dtThis.Rows)
				{
					// check we dont exist (based on our keys) else INSERT
					string strCheck = "SELECT Count([" + dtThis.Columns[0].ColumnName + "]) FROM [" + dtThis.TableName + "]";

					if (nReadOnlyColumnCount > 0)
					{
						strCheck += " WHERE (";
						int nColumnCount1 = 0;

						foreach (DataColumn oColumn in dtThis.Columns)
						{
							nColumnCount1++;

							bool bPrimaryKey = false;

							foreach (DataColumn oColumnKey in dtThis.PrimaryKey)
							{
								if (oColumnKey.ColumnName == oColumn.ColumnName)
									bPrimaryKey = true;
							}

							if (oColumn.ReadOnly || bPrimaryKey)
								strCheck += "[" + oColumn.ColumnName + "]=" + GetValueFromType(oColumn.DataType, oRow[oColumn.ColumnName]);

							if (nColumnCount1 < nReadOnlyColumnCount)
								strCheck += " AND ";
						}

						strCheck += ")";
					}

					// if we exist try an update instead
					if ((int)ExecuteScaler(strCheck) > 0)
					{
						DataSet dsThis = new DataSet(dtThis.TableName);
						dsThis.Tables.Add(dtThis.Clone());
						dsThis.Tables[0].ImportRow(oRow);

						DataItem diThis = new DataItem(dsThis, dataItem.Condition, dataItem.Sort);
						IDataItem di = (IDataItem)diThis;

						Execute(ref di, DataOperation.Update);

						continue;
					}

					string strQueryThis = string.Empty;
					int nColumnCount = 0;

					foreach (DataColumn oColumn in dtThis.Columns)
					{
						nColumnCount++;

						bool bPrimaryKey = false;

						foreach (DataColumn oColumnKey in dtThis.PrimaryKey)
						{
							if (oColumnKey.ColumnName == oColumn.ColumnName)
								bPrimaryKey = true;
						}

						//if (oColumn.ReadOnly)
						//	continue;

						strQueryThis += GetValueFromType(oColumn.DataType, oRow[oColumn.ColumnName]);

						if (nColumnCount < dtThis.Columns.Count)
							strQueryThis += ", ";
					}

					strQueryThis = strQuery.Replace("@VALUES@", strQueryThis);

					// execute
					/*if (bHasIdentity)
						ExecuteNonQuery("SET IDENTITY_INSERT [" + dtThis.TableName + "] ON");*/

					this.ExecuteNonQuery(strQueryThis);

					/*if (bHasIdentity)
						ExecuteNonQuery("SET IDENTITY_INSERT [" + dtThis.TableName + "] OFF");*/
				}
			}
			else if (operation == DataOperation.Update)
			{
				// UPDATE
				strQuery = "UPDATE [" + dtThis.TableName + "] SET ";

				// now execute for each row we are inserting
				foreach (DataRow oRow in dtThis.Rows)
				{
					string strQueryThis = strQuery;
					int nColumnCount = 0;
					int nReadOnlyColumnCount = 0;

					foreach (DataColumn oColumn in dtThis.Columns)
					{
						nColumnCount++;

						bool bPrimaryKey = false;

						foreach (DataColumn oColumnKey in dtThis.PrimaryKey)
						{
							if (oColumnKey.ColumnName == oColumn.ColumnName)
								bPrimaryKey = true;
						}

						if (oColumn.ReadOnly || bPrimaryKey)
						{
							nReadOnlyColumnCount++;
							continue;
						}

						strQueryThis += "[" + oColumn.ColumnName + "]=" + GetValueFromType(oColumn.DataType, oRow[oColumn.ColumnName]);

						if (nColumnCount < dtThis.Columns.Count)
							strQueryThis += ", ";
					}

					// check we are updating at least one column or that at least one column is a key!
					if (nReadOnlyColumnCount == nColumnCount || nReadOnlyColumnCount == 0)
						return;

					// WHERE
					if (dataItem.Condition != null && dataItem.Condition.Length > 0)
						strQueryThis += " WHERE (" + dataItem.Condition + ")";
					else if (nReadOnlyColumnCount > 0)
					{
						strQueryThis += " WHERE (";
						nColumnCount = 0;

						foreach (DataColumn oColumn in dtThis.Columns)
						{
							nColumnCount++;

							bool bPrimaryKey = false;

							foreach (DataColumn oColumnKey in dtThis.PrimaryKey)
							{
								if (oColumnKey.ColumnName == oColumn.ColumnName)
									bPrimaryKey = true;
							}

							if (oColumn.ReadOnly || bPrimaryKey)
								strQueryThis += "[" + oColumn.ColumnName + "]=" + GetValueFromType(oColumn.DataType, oRow[oColumn.ColumnName]);

							if (nColumnCount < nReadOnlyColumnCount)
								strQueryThis += " AND ";
						}

						strQueryThis += ")";
					}

					// execute
					this.ExecuteNonQuery(strQueryThis);
				}
			}
			else if (operation == DataOperation.Create)
			{
				// CREATE TABLE
				// N.B.: the DataTable wrapped in the DataItem only contains DataColumn definitions.
				strQuery = "CREATE TABLE [" + dtThis.TableName + "] (";
				int nColumnCount = 0;

				foreach (DataColumn oColumn in dtThis.Columns)
				{
					strQuery += "\n[" + oColumn.ColumnName + "] ";

					if (oColumn.MaxLength == -1)
						strQuery += GetTypeFromType(oColumn.DataType);
					else
						strQuery += GetTypeFromTypeAndSize(oColumn.DataType, oColumn.MaxLength);

					if (oColumn.AutoIncrement)
						strQuery += " IDENTITY(" + oColumn.AutoIncrementSeed + "," + oColumn.AutoIncrementStep + ")";

					if (oColumn.AllowDBNull)
						strQuery += " NULL";
					else
						strQuery += " NOT NULL";

					// set the default value
					if (oColumn.DefaultValue != DBNull.Value)
						strQuery += " DEFAULT(" + GetDefaultValueFromType(oColumn.DataType, oColumn.DefaultValue) + ")";

					bool bPrimaryKey = false;

					foreach (DataColumn oColumnKey in dtThis.PrimaryKey)
					{
						if (oColumnKey.ColumnName == oColumn.ColumnName)
						{
							bPrimaryKey = true;
							strQuery += " PRIMARY KEY";
						}
					}

					if (!bPrimaryKey && oColumn.Unique)
						strQuery += " UNIQUE";

					if (++nColumnCount < dtThis.Columns.Count)
						strQuery += ", ";
				}

				strQuery += ")";

				ExecuteNonQuery(strQuery);
			}
			else
				throw(new ArgumentException("The DataOperation is not valid", "operation"));
		}

		/// <summary>
		/// Event for mapping data item column names to those on the data source.
		/// </summary>
		/// <remarks>
		/// This event is typically consumed by your Global class and provides a way for to
		/// specify the column names on the data source that map to those in the data item.
		/// </remarks>
		/// <example>
		/// <p>The following example shows how the name mapping can be used.</p>
		/// <p class="lang">[C#]</p>
		/// <code>
		/// Loader.NameMappingCallback += new NameMappingCallbackHandler(Loader_NameMappingCallback);
		/// 
		/// ...
		/// 
		/// public void Loader_NameMappingCallback(string table, ref DataItemDictionary keys)
		/// {
		/// 	keys.Clear();
		/// 
		/// 	switch (table)
		/// 	{
		/// 		case "News":
		/// 			keys.Add("ID", "n_id");
		/// 			keys.Add("Title", "n_title");
		/// 			keys.Add("Description", "n_description");
		/// 			keys.Add("Owner", "n_owner");
		/// 			keys.Add("Created", "n_created");
		/// 			break;
		/// 		case "Product":
		/// 			keys.Add("ID", "pr_id");
		/// 			keys.Add("Name", "pr_name");
		/// 			keys.Add("Description", "pr_description");
		/// 			keys.Add("Price", "pr_price");
		/// 			break;
		/// 	}
		/// }
		/// </code>
		/// </example>
		public event NameMappingCallbackHandler NameMappingCallback;

		#endregion

		private string GetValueFromType(Type type, object value)
		{
			if (value == null || value == DBNull.Value)
				return "NULL";

			switch (type.Name)
			{
				case "String":
					return "'" + S((string)value) + "'";
				case "Int16":
				case "Int32":
				case "Int64":
					return ((int)value).ToString();
				case "Boolean":
					return ((bool)value) ? "1" : "0";
				case "Double":
					return ((double)value).ToString("G");
				case "Single":
					return ((float)value).ToString("G");
				case "Decimal":
					return ((decimal)value).ToString("G");
				case "DateTime":
					return "'" + ((DateTime)value).ToString("dd MMMM yyyy HH:mm:mm") + "'";
			}

			throw new ArgumentException("The System.Type is not supported as a data item on this loader", "type");
		}

		private string GetDefaultValueFromType(Type type, object value)
		{
			if (value == null || value == DBNull.Value)
				return "NULL";

			switch (type.Name)
			{
				case "String":
					return "'" + S((string)value) + "'";
				case "Int16":
				case "Int32":
				case "Int64":
					return ((int)value).ToString();
				case "Boolean":
					return ((bool)value) ? "1" : "0";
				case "Double":
					return ((double)value).ToString("G");
				case "Single":
					return ((float)value).ToString("G");
				case "Decimal":
					return ((decimal)value).ToString("G");
				case "DateTime":
					return "'" + ((DateTime)value).ToString("dd MMMM yyyy HH:mm:mm") + "'";
			}

			throw new ArgumentException("The System.Type is not supported as a data item on this loader", "type");
		}

		private string GetTypeFromType(Type type)
		{
			switch (type.Name)
			{
				case "String":
					return "TEXT";
				case "Int16":
				case "Int32":
				case "Int64":
					return "INT";
				case "Boolean":
					return "BIT";
				case "Double":
				case "Single":
					return "FLOAT";
				case "Decimal":
					return "DECIMAL(18, 6)";
				case "DateTime":
					return "DATETIME";
			}

			throw new ArgumentException("The System.Type is not supported as a data item on this loader", "type");
		}

		private string GetTypeFromTypeAndSize(Type type, int size)
		{
			switch (type.Name)
			{
				case "String":
					return "VARCHAR(" + size + ")";
				case "Int16":
				case "Int32":
				case "Int64":
					return "INT(" + size + ")";
				case "Boolean":
					return "BIT";
				case "Double":
				case "Single":
					return "DOUBLE(" + size + ")";
				case "Decimal":
					return "DECIMAL(18, " + size + ")";
				case "DateTime":
					return "DATETIME";
			}

			throw new ArgumentException("The System.Type is not supported as a data item on this loader", "type");
		}

		private string GetTableFromQuery(string strQuery)
		{
			string strQueryLC = strQuery.ToLower();
			int nStart = 0, nEnd = strQuery.Length;

			if (strQueryLC.IndexOf("select") == 0)
			{
				nStart = strQueryLC.IndexOf("from [", 0) + 6;
				nEnd = strQueryLC.IndexOf("]", nStart);

				return strQuery.Substring(nStart, nEnd - nStart);
			}
			else if (strQueryLC.IndexOf("insert into") == 0)
			{
				nStart = strQueryLC.IndexOf("[", 0) + 1;
				nEnd = strQueryLC.IndexOf("]", nStart);

				return strQuery.Substring(nStart, nEnd - nStart);
			}
			else if (strQueryLC.IndexOf("create table") == 0)
			{
				nStart = strQueryLC.IndexOf("[", 0) + 1;
				nEnd = strQueryLC.IndexOf("]", nStart);

				return strQuery.Substring(nStart, nEnd - nStart);
			}
			else if (strQueryLC.IndexOf("update") == 0)
			{
				nStart = strQueryLC.IndexOf("[", 0) + 1;
				nEnd = strQueryLC.IndexOf("]", nStart);

				return strQuery.Substring(nStart, nEnd - nStart);
			}
			else if (strQueryLC.IndexOf("drop table") == 0)
			{
				nStart = strQueryLC.IndexOf("[", 0) + 1;
				nEnd = strQueryLC.IndexOf("]", nStart);

				return strQuery.Substring(nStart, nEnd - nStart);
			}
			else if (strQueryLC.IndexOf("delete from") == 0)
			{
				nStart = strQueryLC.IndexOf("[", 0) + 1;
				nEnd = strQueryLC.IndexOf("]", nStart);

				return strQuery.Substring(nStart, nEnd - nStart);
			}

			return string.Empty;
		}

		private string S(string str)
		{
			return str.Replace("'", "''");
		}

		#region IDisposable Members

		/// <summary>
		/// Performs tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		/// <remarks>
		/// Calls <see cref="Terminate"/>.
		/// </remarks>
		public void Dispose()
		{
			Terminate();

			// Call GC.SupressFinalize to take this object off the finalization queue and prevent
			// .. finalization code for this object from executing a second time.
			GC.SuppressFinalize(this);
		}

		#endregion

		private void DoTrace(string strCategory, string strMessage)
		{
			try
			{
				System.Web.HttpContext.Current.Trace.Write(strCategory, strMessage);
			}
			catch (Exception)
			{
				System.Diagnostics.Trace.WriteLine(strMessage, strCategory);
			}
		}
	}
}

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
CEO Bttlxe Ltd & Incentica Ltd
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions