DAL Generator using Reflection

Hello, I decided to develop an abstract class that my objects could inherit from and it'd generate Update/Insert/Select/Delete statements for me and I like the way it is structure and works but I do believe that the code itself could be written a little cleaner but since I'm rather inexperience I don't really know everything that is right/wrong so I was wondering if you guys would help me improve my architecture for my DatabaseWrapper.

Here is the code:
---------------------------
TableWrapper.cs
---------------------------

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Data.Common;
using System.Collections;
using System.Reflection;
namespace Devtoo.Data
{
public abstract class TableWrapper
{
public delegate void PropertyChangedHandler(string propName);
public event PropertyChangedHandler PropertyChanged;
List<string> ChangedProperties = new List<string>();
TableIndex primaryKey;
PropertyInfo primaryColumn;
public TableIndex PrimaryKey {
get {
if (primaryKey == null)
SetPrimaryKey();
return primaryKey;
}
}
private void SetPrimaryKey() {
foreach (PropertyInfo property in this.GetType().GetProperties())
{
foreach (object attribute in property.GetCustomAttributes(false))
{
if (attribute.GetType() == typeof(ColumnAttribute))
{
ColumnAttribute temp = (ColumnAttribute)attribute;
if (temp.PrimaryKey)
primaryKey = new TableIndex(temp.ColumnName, temp.Type, property.GetValue(this, null), temp.IsNullable);
}
}
}
}
public abstract string TableName {
get;
}
public TableWrapper() {
this.PropertyChanged += new PropertyChangedHandler(TableWrapper_PropertyChanged);
}
public virtual void OnPropertyChange(string prop) {
this.PropertyChanged(prop);
}
void TableWrapper_PropertyChanged(string propName) {
this.ChangedProperties.Add(propName);
}
public virtual void Update() {
List<TableIndex> update = new List<TableIndex>();
string columns;
foreach (KeyValuePair<TableIndex, bool> pair in GetColumns())
{
if (pair.Value == true)
{
if (!IsNull(pair.Key) && pair.Key != PrimaryKey)
update.Add(pair.Key);
}
}
TableIndex[] updateBy = update.ToArray();
GenerateWhereClause(updateBy, out columns);
columns = columns.Replace("AND", ",");
string query = String.Format("UPDATE {0} SET {1} WHERE {2} = {3}",
this.TableName, columns, this.PrimaryKey.ColumnName, this.PrimaryKey.Value);
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(query);
GenerateParameters(updateBy, ref db, ref command);
db.ExecuteNonQuery(command);
this.ChangedProperties.Clear();
}
public virtual void Fetch() {
string where;

List<TableIndex> list = new List<TableIndex>();
foreach (KeyValuePair<TableIndex, bool> pair in GetColumns())
{
list.Add(pair.Key);
}
TableIndex[] searchBy = list.ToArray();
GenerateWhereClause(searchBy, out where);
string query = String.Format("SELECT * FROM {0} WHERE {1}", this.TableName, where);
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetSqlStringCommand(query);
GenerateParameters(searchBy, ref db, ref command);
IDataReader reader = db.ExecuteReader(command);
if (reader.Read())
{
foreach (PropertyInfo propinfo in this.GetType().GetProperties())
{
object[] attributes = propinfo.GetCustomAttributes(false);
foreach (object attribute in attributes)
{
if (attribute.GetType() == typeof(ColumnAttribute))
{
ColumnAttribute temp = (ColumnAttribute)attribute;
int ord = reader.GetOrdinal(temp.ColumnName);
propinfo.SetValue(this, reader.GetValue(ord), null);
}
}
}
}
ChangedProperties.Clear();
}
public virtual void Insert() {
string columns;
string values;
List<TableIndex> list = new List<TableIndex>();
foreach (KeyValuePair<TableIndex, bool> pair in GetColumns())
{
if (pair.Key != PrimaryKey)
list.Add(pair.Key);
}

TableIndex[] insert = list.ToArray();
GenerateColumnsForInsert(insert, out columns, out values);
Database db = DatabaseFactory.CreateDatabase();
string query = String.Format(@"INSERT INTO {0}({1}) VALUES({2})
SET @PrimaryKey = SCOPE_IDENTITY()
SELECT @PrimaryKey", this.TableName, columns, values);
DbCommand command = db.GetSqlStringCommand(query);
db.AddInParameter(command, "@PrimaryKey", this.PrimaryKey.Type);
GenerateParameters(insert, ref db, ref command);
this.primaryColumn.SetValue(this, db.ExecuteScalar(command), null);
ChangedProperties.Clear();
}
public bool Delete() {
if (Exists())
{
Database db = DatabaseFactory.CreateDatabase();
string query = String.Format("DELETE FROM {0} WHERE {1} = {2}", this.TableName, this.PrimaryKey.ColumnName, this.PrimaryKey.Value);
DbCommand command = db.GetSqlStringCommand(query);
if (db.ExecuteNonQuery(command) > 0)
return true;
}
return false;
}

private bool Exists() {
Database db = DatabaseFactory.CreateDatabase();
string query = String.Format("SELECT TOP 1 1 FROM {0}", this.TableName);
DbCommand command = db.GetSqlStringCommand(query);
object result = db.ExecuteScalar(command);
if (result != null)
return true;
else
return false;
}
private bool IsNull(TableIndex index) {
bool isNull = false;
if (index.Value == null && !index.IsNullable)
isNull = true;
else
if (index.Value.GetType() == typeof(DateTime) && (DateTime)index.Value == DateTime.MinValue)
isNull = true;
else if (index.Value.GetType() == typeof(int) && (Int32)index.Value == 0)
isNull = true;
return isNull;
}
private Dictionary<TableIndex, bool> GetColumns() {
Dictionary<TableIndex, bool> columns = new Dictionary<TableIndex, bool>();
PropertyInfo[] propertyInfo = this.GetType().GetProperties();
foreach (PropertyInfo property in propertyInfo)
{
foreach (object attribute in property.GetCustomAttributes(false))
{
if (attribute.GetType() == typeof(ColumnAttribute))
{
ColumnAttribute column = (attribute as ColumnAttribute);
object val = property.GetValue(this, null);
TableIndex temp = new TableIndex(column.ColumnName, column.Type, val, column.IsNullable);
if (column.PrimaryKey)
{
primaryKey = temp;
primaryColumn = property;
}
if (!IsNull(temp))
columns.Add(temp, ChangedProperties.Contains(property.Name));
}
}
}
return columns;
}
private void GenerateParameters(TableIndex[] columns, ref Database db, ref DbCommand command) {
foreach (TableIndex index in columns)
{
db.AddInParameter(command, "@" + index.ColumnName, index.Type, index.Value);
}
}
private void GenerateWhereClause(TableIndex[] searchBy, out string where) {
where = String.Empty;
int count = 1;
foreach (TableIndex index in searchBy)
{
where += String.Format("{0} = @{0}", index.ColumnName);
if (count != searchBy.Length)
where += " AND ";
count++;
}
}
private void GenerateColumnsForInsert(TableIndex[] indexes, out string columns, out string values) {
columns = String.Empty;
values = String.Empty;
foreach (TableIndex index in indexes)
{
columns += String.Format(",{0}", index.ColumnName);
values += String.Format(",@{0}", index.ColumnName);
}
columns = columns.Substring(1);
values = values.Substring(1);
}
}
}

-----------------
TableIndex.cs
-----------------

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace Devtoo.Data
{
public class TableIndex
{
private string columnName;
public string ColumnName {
get { return columnName; }
set { this.columnName = value; }
}
private object value;
public object Value {
get { return value; }
set { this.value = value; }
}
private DbType type;
public DbType Type {
get { return type; }
set { type = value; }
}
private bool isNullable;
public bool IsNullable {
get { return isNullable; }
set { isNullable = value; }
}

public TableIndex(string columnName, DbType type, object value, bool isNullable) {
this.columnName = columnName;
this.type = type;
this.value = value;
}
}
}
----------------------
ColumnAttribute.cs
----------------------

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace Devtoo.Data
{
[global::System.AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false)]
public sealed class ColumnAttribute : Attribute
{
private string columnName;
public string ColumnName {
get { return columnName; }
set { columnName = value; }
}
private DbType type;
public DbType Type {
get { return type; }
set { type = value; }
}
private bool isNullable;
public bool IsNullable {
get { return isNullable; }
set { isNullable = value; }
}
private bool primaryKey;
public bool PrimaryKey {
get { return primaryKey; }
set { primaryKey = value; }
}

public ColumnAttribute(string name, DbType type, bool nullable) {
this.columnName = name;
this.type = type;
this.isNullable = nullable;
}
public ColumnAttribute(string name, DbType type, bool nullable, bool primaryKey)
: this(name, type, nullable) {
this.primaryKey = primaryKey;
}
}
}


--------------------
Example useage
--------------------

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Data.Common;
using System.Data;
namespace Devtoo.Data
{
public class Department : TableWrapper
{
public override string TableName {
get {
return "HumanResources.Department";
}
}
private int departmentId;
[ColumnAttribute("DepartmentID", DbType.Int32, false, true)]
public int DepartmentId {
get { return departmentId; }
set {
departmentId = value;
}
}

private string name;
[ColumnAttribute("Name", DbType.String, false)]
public string Name {
get { return name; }
set {
name = value;
OnPropertyChange("Name");
}
}
private string groupName;
[ColumnAttribute("GroupName", DbType.String, false)]
public string GroupName {
get { return this.groupName; }
set {
this.groupName = value;
OnPropertyChange("GroupName");
}
}
private DateTime modifiedDate;
[ColumnAttribute("ModifiedDate", DbType.DateTime, false)]
public DateTime ModifiedDate {
get { return modifiedDate; }
set {
modifiedDate = value;
OnPropertyChange("ModifiedDate");
}
}

public Department() { }
public Department(int id) {
this.PrimaryKey.Value = id;
}
}
}




Answer this question

DAL Generator using Reflection

  • Anonymousfff

    Hi Sontac,

    i did not realy study your code, but I did something similar in java about 4 years ago and had a lot of performance issues. We build web content management systems and used such persistence base-classes for crud-operation.

    The performance issues arised, when selecting a huge amount of data, since we iterated over the result-records and iterated over the properties of our bean, which was very bad in terms of performance. I had to invested alot of time and developed on the fly java byte-code generation for the task of filling the object with data.

    It's possibly not an issue using .net or a newer jdk. But just be aware it could become an aproblem.

    I personaly tend to use code-generators, since you don't have to look up ervery property via reflection.

    Cheers

    Marcus Lankenau


  • DAL Generator using Reflection