using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using WmData;
using System.Reflection;
using System.Diagnostics;
using MySql.Data.MySqlClient;
namespace WmData {
///
/// WmData: The ADO.NET based object-to-SQL and SQL-to-object system
/// Original data was part of the Manager system before it
/// was used in systems like WMCM and WMDS. That data was based
/// upon the original data as created on Curacao october 2004.
/// The first version of this library was based upon the
/// Secure Checkout version.
/// Original WmData created at 18th of October 2007
///
/// Updated to conform WmGenericData at 3rd of August 2010
///
public class WmData : WmGenericData {
protected WmDataAdapterManager dataAdapterManager ;
protected object adapterLinkLock = new object();
protected DataSet wmDataSet;
protected WmDataFileSupport fileSupport;
public WmData(string databaseUsername, string databasePassword, string databaseHost, int databasePort, string databaseCatalog)
: base(databaseUsername,databasePassword,databaseHost,databasePort,databaseCatalog) {
Trace.WriteLine("WmData initiated for " + databaseCatalog+ " on host "+databaseHost);
}
public WmData(string databaseUsername, string databasePassword, string databaseHost, int databasePort, string databaseCatalog, string thisServerAddress, List dataLinkServers, int syncIntervalInSeconds, ListsyncObjectTypes)
: base(databaseUsername, databasePassword, databaseHost, databasePort, databaseCatalog,thisServerAddress, dataLinkServers,syncIntervalInSeconds,syncObjectTypes) {
Trace.WriteLine("WmData initiated for " + databaseCatalog + " on host " + databaseHost);
}
///
/// Reset the data set, live link, id manager and file support
///
public override void reset() {
if (wmDataSet != null) { wmDataSet.Dispose(); }
if (dataAdapterManager != null) { dataAdapterManager = null; }
if (idManager != null) { idManager = null; }
if (fileSupport != null) { fileSupport = null; }
wmDataSet = new DataSet("wmdata");
dataAdapterManager = new WmDataAdapterManager(this, dataBaseCredentials);
idManager = new WmDataIdManager(dataBaseCredentials);
fileSupport = new WmDataFileSupport(this, idManager, dataBaseCredentials);
}
///
/// Checks if object has a database adapter and SQL table.
/// This is required for new objects or the retrieval of objects, you should not call this method when updating.
///
///
public void checkDataAdapter(Type objectType) {
string tableName = WmDataTools.getTableName(objectType);
lock (adapterLinkLock) {
if (!dataAdapterManager.isAdapted(tableName)) {
dataAdapterManager.createAdapterForObject(objectType);
}
}
}
#region ADD and IMPORT: addDataRow, importDataRow, addObject
///
/// Add a data row and automatically assigns a new id
///
public void addDataRow(DataRow newRow) {
addDataRow(newRow, false);
}
///
/// Add a data row and specify to override the auto id
///
public void addDataRow(DataRow newRow, bool overrideAutoId) {
addDataRow(newRow, overrideAutoId, -1);
}
///
/// Add a data row using and specify override auto id and reference transaction moment
///
public override void addDataRow(Type objectType, DataRow addRow, bool overrideAutoId, long referenceTransactionMomentUtcTics) {
addDataRow(addRow, overrideAutoId, referenceTransactionMomentUtcTics);
}
///
/// Add a data row, specify override auto id and reference transaction moment
///
public void addDataRow(DataRow newRow, bool overrideAutoId, long referenceTransactionMomentUtcTicks) {
string tableName = WmDataTools.getTableName(newRow);
DataTable addTable = WmDataSet.Tables[tableName];
lock (addTable) {
int givenId = -1;
if (!overrideAutoId) {
givenId = idManager.getNewId(tableName);
newRow["id"] = givenId;
}
addTable.Rows.Add(newRow);
dataAdapterManager.update(tableName);
}
raiseDataChangeEvent(tableName, WmDataRowAction.ADD, (int)newRow["id"]);
if (transactionManager != null) {
transactionManager.registerInsertOrUpdateTransaction(tableName,newRow,WmDataRowAction.ADD,referenceTransactionMomentUtcTicks);
}
}
///
/// Import a given data row, automatically assigns an Id
///
public void importDataRow(DataRow importRow) {
importDataRow(importRow, false);
}
///
/// Import a given data row and specify override auto id
///
public void importDataRow(DataRow importRow, bool overrideAutoId) {
importDataRow(importRow, overrideAutoId, -1);
}
///
/// Import a given data row and specify override auto id and reference transaction moment
///
public override void importDataRow(Type objectType, DataRow importRow, bool overrideAutoId, long referenceTransactionMomentUtcTicks) {
importDataRow(importRow, overrideAutoId, referenceTransactionMomentUtcTicks);
}
///
/// Import a given data row and specify override auto id and reference transaction moment
///
public void importDataRow(DataRow importRow, bool overrideAutoId, long referenceTransactionMomentUtcTicks) {
string tableName = WmDataTools.getTableName(importRow);
DataTable importTable = WmDataSet.Tables[tableName];
lock (importTable) {
int givenId = -1;
if (!overrideAutoId) {
givenId = idManager.getNewId(tableName);
importRow["id"] = givenId;
}
importTable.ImportRow(importRow);
// Reset the row state
importRow.AcceptChanges();
importRow.SetAdded(); // will cause the backend to add this row to the sql server
dataAdapterManager.update(tableName);
}
raiseDataChangeEvent(tableName, WmDataRowAction.ADD, (int)importRow["id"]);
if (transactionManager != null) {
transactionManager.registerInsertOrUpdateTransaction(tableName,importRow,WmDataRowAction.ADD,referenceTransactionMomentUtcTicks);
}
}
///
/// Add an object and specify override auto id and reference transaction moment
///
public override void addObject(object objectInstance, bool overrideAutoId, long referenceTransactionMomentUtcTicks) {
if (objectInstance is WmDataFile) { throw new Exception("WmDataFiles should only be handled by the WmDataFileSupport object"); }
Type objectType = objectInstance.GetType();
WmIntermediateObject intermediateObject = getIntermediateObject(objectType);
string tableName = intermediateObject.TableName;
// Check if object exists in database backend and check if it is adapted correctly
checkDataAdapter(objectType);
int givenId = -1;
DataTable addTable = WmDataSet.Tables[tableName];
lock (addTable) {
// Create a new row
DataRow newRow = addTable.NewRow();
// Set values of object to corresponding columns in newly created row:
updateRowValuesFromObjectProperties(newRow, objectInstance,intermediateObject);
// Now workout the possible auto id of the new row:
if (!overrideAutoId) {
givenId = idManager.getNewId(tableName);
intermediateObject.IdProperty.Setter(objectInstance, givenId);
newRow["id"] = givenId;
} else {
givenId = intermediateObject.getObjectInstanceId(objectInstance);
newRow["id"] = givenId;
// Override of auto id was request, check if row isn't already present:
DataRow conflictingRow = getDataRow(addTable, givenId);
if (conflictingRow != null) { // OOPS! Conflicting id was found, check if rows arent a duplicate...
if (isDuplicateRow(newRow, conflictingRow)) {
// TODO: Report this by throwing internal event
Trace.WriteLine("WmData: Ignored duplicate entry of object in table=" + tableName + " id=" + newRow["id"]);
return; // ignore the duplicate, none gets hurt..
} else {
// Conflicting row was found and was not a duplicate, data mismatch so it seems:
throw new Exception("WmData: Duplicate entry found, table=" + tableName + " id=" + newRow["id"]);
}
}
}
// Add the object instance to the row based object cache:
newRow["objectinstance"] = objectInstance;
addTable.Rows.Add(newRow); // add the row to the table
dataAdapterManager.update(tableName); // update the database backend
}
raiseDataChangeEvent(tableName, WmDataRowAction.ADD, givenId);
if (transactionManager != null) {
transactionManager.registerInsertOrUpdateTransaction(intermediateObject,givenId,WmDataRowAction.ADD,objectInstance,referenceTransactionMomentUtcTicks);
}
}
#endregion
#region UPDATE: updateDataRow, updateObject
///
/// Update the data row
///
public void updateDataRow(DataRow updateRow) {
updateDataRow(updateRow, -1);
}
///
/// Update a data row and specify the reference transaction moment
///
public override void updateDataRow(Type objectType, DataRow updateRow, long referenceTransactionMomentUtcTicks) {
updateDataRow(updateRow, referenceTransactionMomentUtcTicks);
}
///
/// Update a data row and specify the reference transaction moment
///
public void updateDataRow(DataRow updateRow, long referenceTransactionMomentUtcTicks) {
string tableName = WmDataTools.getTableName(updateRow);
DataTable updateTable = WmDataSet.Tables[tableName];
lock (updateTable) {
updateRow.EndEdit();
dataAdapterManager.update(tableName);
}
raiseDataChangeEvent(tableName, WmDataRowAction.UPDATE, (int)updateRow["id"]);
if (transactionManager != null) {
transactionManager.registerInsertOrUpdateTransaction(tableName,updateRow,WmDataRowAction.UPDATE,referenceTransactionMomentUtcTicks);
}
}
///
/// Update an object and specify the reference transaction moment
///
public override void updateObject(object objectInstance, long referenceTransactionMomentUtcTicks) {
if (objectInstance is WmDataFile) { throw new Exception("WmDataFiles should only be handled by the WmDataFileSupport object"); }
Type objectType = objectInstance.GetType();
bool objectWasChanged = false;
WmIntermediateObject intermediateObject = getIntermediateObject(objectType);
int objectId = intermediateObject.getObjectInstanceId(objectInstance);
string tableName = WmDataTools.getTableName(objectType);
DataTable updateTable = WmDataSet.Tables[tableName];
lock (updateTable) {
DataRow updateRow = getDataRow(updateTable, objectId);
if (updateRow == null) {
Trace.WriteLine("WmData: Request to update a non existing object. table=" + tableName + " objectid=" + objectId);
return; // no one gets hurt, perhaps we should do this to prevent these kinds of bogus calls...
} else {
updateRow.BeginEdit();
if (updateRowValuesFromObjectProperties(updateRow, objectInstance,intermediateObject)) {
objectWasChanged = true; // indicating something was in fact changed
updateRow.EndEdit(); // Stop editing row, causing row to be marked as edited
dataAdapterManager.update(tableName); // write the changes back to SQL backend, only this row will be updated
} else {
// the original row and the object had the same values, ignore this by not performing backend SQL update
updateRow.CancelEdit();
objectWasChanged = false;
Trace.WriteLine("WmData: Ignored obsolete update, DataRow was already the same as object properties. Table " + tableName + " objectId=" + objectId);
}
}
}
if (objectWasChanged) {
raiseDataChangeEvent(tableName, WmDataRowAction.UPDATE, objectId);
}
if ((objectWasChanged || referenceTransactionMomentUtcTicks > 0) && transactionManager!=null) {
transactionManager.registerInsertOrUpdateTransaction(intermediateObject, objectId, WmDataRowAction.UPDATE, objectInstance, referenceTransactionMomentUtcTicks);
}
}
#endregion
#region DELETE: deleteDataRow, deleteObject, deleteTableContents
///
/// Delete a data row
///
public void deleteDataRow(DataRow row) {
deleteDataRow(row,-1); // event fires there, not here
}
///
/// Delete a data row and specify the reference transaction moment
///
public void deleteDataRow(DataRow row, long referenceTransactionMomentUtcTicks) {
string tableName = row.Table.TableName;
int id = (int)row["id"];
deleteObject(tableName, id, referenceTransactionMomentUtcTicks); // event fires there, not here.
}
///
/// Delete a data row and specify the reference transaction moment
///
public override void deleteDataRow(Type objectType, DataRow deleteRow, long referenceTransactionMomentUtcTicks) {
deleteDataRow(deleteRow, referenceTransactionMomentUtcTicks);
}
///
/// Delete an object and specify the reference tranasaction moment
///
public override void deleteObject(Type objectType, int id, long referenceTransactionMomentUtcTicks) {
deleteObject(WmDataTools.getTableName(objectType), id, -1); // event fires there, not here.
}
///
/// Delete an object and specify the reference transaction moment
///
public void deleteObject(string tableName, int objectId, long referenceTransactionMomentUtcTicks) {
if (tableName.Equals(WmDataFileSupport.TableName)) { throw new Exception("WmDataFiles should only be handled by the WmDataFileSupport object"); }
DataTable delTable = WmDataSet.Tables[tableName];
lock (delTable) {
DataRow deleteRow = getDataRow(delTable, objectId);
if (deleteRow != null) { // Check if row still exists
deleteRow.Delete(); // will mark the row as to be deleted
dataAdapterManager.update(tableName); // will perform the row removal on DataBase backend
} else {
Trace.WriteLine("WmData: Ignored attempt to remove non existant object. Table " + tableName + " objectId=" + objectId);
}
}
raiseDataChangeEvent(tableName, WmDataRowAction.DELETE, objectId);
if (transactionManager != null) {
transactionManager.registerDeleteTransaction(tableName,objectId,referenceTransactionMomentUtcTicks);
}
}
///
/// Delete table contents (each and every row)
///
public override void deleteTableContents(Type objectType) {
string tableName = WmDataTools.getTableName(objectType);
checkDataAdapter(objectType);
DataTable delTable = WmDataSet.Tables[tableName];
lock (delTable) {
DataRow[] allRows = delTable.Select();
for(int r=0; r
/// Get data row matching the select
///
public override DataRow getDataRow(Type objectType, string select) {
return getDataRow(WmDataTools.getTableName(objectType), select);
}
///
/// Get data row matching the select
///
public DataRow getDataRow(string tableName, string select) {
DataTable table = WmDataSet.Tables[tableName];
return getDataRow(table, select);
}
///
/// Get data row matching the select
///
public DataRow getDataRow(DataTable table, string select) {
lock (table) {
DataRow[] tmpResult = table.Select(select);
if (tmpResult.Length > 0) {
return tmpResult[0];
} else {
return null;
}
}
}
///
/// Get data row matching the specified id
///
public override DataRow getDataRow(Type objectType, int id) {
DataTable table = WmDataSet.Tables[WmDataTools.getTableName(objectType)];
return getDataRow(table, id);
}
///
/// Get data row matching the specified id
///
public DataRow getDataRow(DataTable table, int rowId) {
return table.Rows.Find(rowId); // find the row matching the rowId as primary key
}
///
/// Get data rows matching the select
///
public DataRow[] getDataRows(string selectTableName, string select) {
return getDataRows(selectTableName, select, null);
}
///
/// Get data rows matching the select
///
public DataRow[] getDataRows(DataTable selectTable, string select) {
return getDataRows(selectTable, select, null);
}
///
/// Get data rows matching the select, sorted as specified
///
public override DataRow[] getDataRows(Type objectType, string select,string sort) {
checkDataAdapter(objectType);
string tableName = WmDataTools.getTableName(objectType);
return getDataRows(tableName, select,sort);
}
///
/// Get data rows matching the select, sorted as specified
///
public DataRow[] getDataRows(string selectTableName, string select, string sort) {
DataTable selectTable = WmDataSet.Tables[selectTableName];
return getDataRows(selectTable, select, sort);
}
///
/// Get data rows matchign the select, sorted as specified
///
public DataRow[] getDataRows(DataTable selectTable, string select, string sort) {
lock (selectTable) {
return selectTable.Select(select, sort);
}
}
///
/// Get object matching the specified id
///
public override object getObject(Type objectType, int id) {
object returnObject = null;
string tableName = WmDataTools.getTableName(objectType);
checkDataAdapter(objectType);
DataTable selectTable = WmDataSet.Tables[tableName];
lock (selectTable) {
DataRow result = getDataRow(selectTable, id);
if (result != null) {
checkObjectCacheInRow(result, objectType);
returnObject = result["objectinstance"];
}
}
return returnObject;
}
///
/// Get object matching the select
///
public override object getObject(Type objectType, string select) {
object returnObject = null;
string tableName = WmDataTools.getTableName(objectType);
checkDataAdapter(objectType);
DataTable selectTable = WmDataSet.Tables[tableName];
lock (selectTable) {
DataRow[] results = getDataRows(selectTable, select);
if (results.Length > 0) {
checkObjectCacheInRow(results[0], objectType);
returnObject = results[0]["objectinstance"];
}
}
return returnObject;
}
///
/// Get objects matching the select
///
public override ArrayList getObjects(Type objectType, string select) {
string tableName = WmDataTools.getTableName(objectType);
if (tableName.Equals("runtimetype")) { throw new Exception("WmData: Select Object was a System.Type"); }
if (tableName.Equals(WmDataFileSupport.TableName)) { throw new Exception("WmDataFiles should only be handled by the WmDataFileSupport object"); }
checkDataAdapter(objectType);
DataTable selectTable = WmDataSet.Tables[tableName];
ArrayList objects = new ArrayList();
lock (selectTable) {
DataRow[] result;
lock (selectTable) {
result = getDataRows(selectTable, select);
for (int i = 0; i < result.Length; i++) {
checkObjectCacheInRow(result[i], objectType);
objects.Add(result[i]["objectinstance"]);
}
}
}
return objects;
}
///
/// Get the amount of rows for this object type
///
public override int getRowCount(Type objectType) {
string tableName = WmDataTools.getTableName(objectType);
int rowCount = 0;
checkDataAdapter(objectType);
DataTable selectTable = WmDataSet.Tables[tableName];
lock (selectTable) {
rowCount = selectTable.Rows.Count;
}
return rowCount;
}
#endregion
///
/// Create an object instance if necessary
///
protected void checkObjectCacheInRow(DataRow row, Type objectType) {
if (row["objectinstance"] is DBNull) {
row["objectinstance"] = getObjectInstanceFromRow(objectType, row);
row.AcceptChanges(); // Otherwise this row is marked as updated and SQL backend will be forced to update this, bad for large tables
}
}
///
/// Data File Support
///
public WmDataFileSupport FileSupport{
get { return fileSupport; }
}
///
/// The ADO.NET data set used by this data instance
///
public DataSet WmDataSet {
get { return wmDataSet; }
set { wmDataSet = value; }
}
}
}