none
DAL Generator using Reflection RRS feed

  • Question

  • 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;
            }
        }
    }

    Wednesday, April 26, 2006 10:27 AM

All replies

  • 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

     

    Monday, May 8, 2006 3:14 PM
  • More code, please. Thanks.
    http://www.alhambra-eidos.es/web2005/index.html
    Friday, February 13, 2009 5:52 PM
  • As somebody has said before in the discussion, you should use a DAL generator tool like Tierdeveloper,Codus etc which will create the DAL layer automatically for you.They also have a feature of retaining the custom code while reengineering the code(regenerating the code whenever the database changes), optimistic and pessimistic concuurrency issues handled automatically, entity and DAL clasess generated automatically from database,creating the joins and domain layer accordingly and many other things...

    Would advise you to look to these type of tools...


    Hope this helps!!

    Cheers
    TA

    -------------------------------
    If this help, please mark this as answer
    Monday, February 16, 2009 6:49 AM