Click here to Skip to main content
Click here to Skip to main content
Articles » Web Development » ASP.NET » General » Downloads
 
Add your own
alternative version

Implementing Model-View-Presenter in ASP.NET

, 17 Nov 2007 CPOL
Three implementations of Model-View-Presenter in ASP.NET 2.0.
MVPSampleApp.zip
MVP.SampleApp
Lib
Microsoft.Practices.EnterpriseLibrary.Common.dll
Microsoft.Practices.EnterpriseLibrary.Data.dll
MySql.Data.dll
nunit.framework.dll
Rhino.Mocks.dll
Model
Data
Interfaces
Properties
Presentation
Presentation.Tests
Properties
Interfaces
Properties
SubSonic
ActiveRecord
Builder
CodeGeneration
Templates
CodeLanguage
Configuration
Controls
Calendar
lang
skin
active-bg.gif
calendar.gif
dark-bg.gif
hover-bg.gif
menuarrow.gif
normal-bg.gif
rowhover-bg.gif
status-bg.gif
title-bg.gif
today-bg.gif
Resources
DataProviders
Properties
Sql Tools
SubSonic.snk
Sugar
WebApp
App_Data
Views
SQL2000SampleDb.zip
SQL2000SampleDb.msi
using System;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Text;
using System.Xml.Serialization;
using SubSonic.Utilities;
using System.Web.UI.WebControls;

namespace SubSonic
{
    /// <summary>
    /// Base class for persisting objects. Follows the "Active Record Design Pattern".
    /// You can read more on this pattern at http://en.wikipedia.org/wiki/Active_Record
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable]
    public abstract class AbstractRecord<T> where T : AbstractRecord<T>, new()
    {
        #region State Properties

        public virtual void Initialize()
        {
        }

        protected virtual void Loaded()
        {
        }

        private bool _isLoaded = false;
        [XmlIgnore]
        [HiddenForDataBinding(true)]
        public bool IsLoaded
        {
            get { return _isLoaded; }
            set { _isLoaded = value; }
        }

        internal bool _isNew = true; // ML Fix: VB is case insensitive; field name must not be the same as the property name.

        /// <summary>
        /// True if data in the object needs to be saved
        /// </summary>
        [XmlIgnore]
        [HiddenForDataBinding(true)]
        public bool IsNew
        {
            get { return _isNew; }
            set { _isNew = value; }
        }


        /// <summary>
        /// Automatically called upon object creation. Sets IsNew to true;
        /// </summary>
        public void MarkNew()
        {
            _isNew = true;
        }

        /// <summary>
        /// True if data in the object has been changed and differs from DB.
        /// </summary>
        [XmlIgnore]
        [HiddenForDataBinding(true)]
        public bool IsDirty
        {
            get { return columnSettings.IsDirty; }
        }

        /// <summary>
        /// Called after any property is set. Sets IsDirty to True.
        /// </summary>
        public void MarkClean()
        {
            columnSettings.IsDirty = false;
        }

        public void MarkOld()
        {
            _isNew = false;
        }

        private string tableName;

        /// <summary>
        /// Name of the table
        /// </summary>
        [XmlIgnore]
        [HiddenForDataBinding(true)]
        public string TableName
        {
            get { return tableName; }
            protected set { tableName = value; }
        }

        #endregion

        #region Object Overrides

        public string Inspect()
        {
            return Inspect(true);
        }

        public string Inspect(bool useHtml)
        {
            StringBuilder sb = new StringBuilder();
            string sOut;
            if(useHtml)
            {
                sb.Append("<table><tr><td colspan=2><h3>" + BaseSchema.Name + " Inspection</h3></td></tr>");

                foreach(TableSchema.TableColumn col in BaseSchema.Columns)
                {
                    sb.Append("<tr><td><span style=\"font-weight:bold\">" + col.ColumnName + "</span></td><td>" + GetColumnValue<object>(col.ColumnName) + "</td></tr>");
                }
                sb.Append("</table>");
                sOut = sb.ToString();
            }
            else
            {
                sb.AppendLine("#################" + BaseSchema.Name + " Inspection ####################");

                foreach(TableSchema.TableColumn col in BaseSchema.Columns)
                {
                    sb.AppendLine(col.ColumnName + ": " + GetColumnValue<object>(col.ColumnName));
                }
                sb.AppendLine("#############################################################################");
                sOut = sb.ToString();
            }
            return sOut;
        }


        public override string ToString()
        {
            //return the value in the second column
            //as by our convention, this is the "descriptor" column.
            string result = "";
            if(columnSettings != null)
            {
                if(columnSettings.Count > 1)
                {
                    result = columnSettings[1].CurrentValue.ToString();
                }
                else if(columnSettings.Count == 1)
                {
                    result = columnSettings[0].CurrentValue.ToString();
                }
            }
            return result;
        }

        #endregion

        #region DB Properties/Methods

        /// <summary>
        /// Returns a default setting per data type
        /// </summary>
        /// <param name="column"></param>
        /// <returns></returns>
        protected static object GetDefaultSetting(TableSchema.TableColumn column)
        {
            return Utility.GetDefaultSetting(column);
        }

        public DbType GetDBType(string columnName)
        {
            TableSchema.TableColumn col = BaseSchema.GetColumn(columnName);
            return col.DataType;
        }

        protected static TableSchema.Table table;

        /// <summary>
        /// The base static class that holds all schema info for the table.
        /// The class must be instanced at least once to populate this table.
        /// </summary>
        protected static TableSchema.Table BaseSchema
        {
            get
            {
                if(table == null)
                    //if (!IsSchemaInitialized)
                {
                    new T();
                }
                return table;
            }
            set { table = value; }
        }

        protected static bool IsSchemaInitialized
        {
            get { return (table != null && table.Columns != null && table.Columns.Count > 0); }
        }

        /// <summary>
        /// Sets the Primary Key of the object
        /// </summary>
        /// <param name="oValue"></param>
        protected void SetPrimaryKey(object oValue)
        {
            columnSettings.SetValue(BaseSchema.PrimaryKey.ColumnName, oValue);
        }

        /// <summary>
        /// Returns the current value of the primary key
        /// </summary>
        /// <returns></returns>
        public object GetPrimaryKeyValue()
        {
            //return columnSettings.GetValue(table.PrimaryKey.ColumnName).ToString();
            return columnSettings.GetValue<object>(BaseSchema.PrimaryKey.ColumnName);
        }

        /// <summary>
        /// The column settings hold the current values of the object in a collection
        /// so that reflection is not needed in the base class to fill out the commands
        /// </summary>
        private TableSchema.TableColumnSettingCollection columnSettings = null;

        public void SetColumnValue(string columnName, object oValue)
        {
            if(columnSettings == null)
            {
                columnSettings = new TableSchema.TableColumnSettingCollection();
            }

            columnSettings.SetValue(columnName, oValue);

        }

        /// <summary>
        /// Returns the current value of a column.
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        public CT GetColumnValue<CT>(string columnName)
        {
            CT oOut = default(CT);

            if(columnSettings != null)
            {
                oOut = columnSettings.GetValue<CT>(columnName);
            }

            return oOut;
        }


        private string _providerName;

        [XmlIgnore]
        [HiddenForDataBinding(true)]
        public string ProviderName
        {
            get { return _providerName; }
            protected set { _providerName = value; }
        }

        public TableSchema.Table GetSchema()
        {
            return BaseSchema;
        }

        #endregion

        public static Query Query()
        {
            new T();
            return new Query(table);
        }

        protected virtual void SetDefaults()
        {
            //initialize to default settings

            bool setDefault = true;
            bool setDbDefaults = DataService.GetInstance(ProviderName).SetPropertyDefaultsFromDatabase;

            if(DataService.GetInstance(ProviderName).CurrentSharedConnection != null)
            {
                setDefault = DataService.GetInstance(ProviderName).CurrentSharedConnection.State == ConnectionState.Closed;
            }
            if (setDefault)
            {
                foreach(TableSchema.TableColumn col in BaseSchema.Columns)
                {
                    if(setDbDefaults && !String.IsNullOrEmpty(col.DefaultSetting))
                    {
                        if(!Utility.IsMatch(col.DefaultSetting, SqlSchemaVariable.DEFAULT))
                        {
                            QueryCommand cmdDefault = new QueryCommand(SqlFragment.SELECT + col.DefaultSetting, col.Table.Provider.Name);
                            SetColumnValue(col.ColumnName, DataService.ExecuteScalar(cmdDefault));
                        }
                    }
                    else
                    {
                        SetColumnValue(col.ColumnName, Utility.GetDefaultSetting(col));
                    }
                }
            }
            Initialize();
        }

        protected void ForceDefaults()
        {
            foreach (TableSchema.TableColumn col in BaseSchema.Columns)
            {
                if (!String.IsNullOrEmpty(col.DefaultSetting))
                {
                    if (!Utility.IsMatch(col.DefaultSetting, SqlSchemaVariable.DEFAULT))
                    {
                        QueryCommand cmdDefault = new QueryCommand(SqlFragment.SELECT + col.DefaultSetting, col.Table.Provider.Name);
                        SetColumnValue(col.ColumnName, DataService.ExecuteScalar(cmdDefault));
                    }
                }
                else
                {
                    SetColumnValue(col.ColumnName, Utility.GetDefaultSetting(col));
                }
            }
        }

        public void LoadByParam(string columnName, object paramValue)
        {
            MarkOld();
            IDataReader rdr = null;
            try
            {
                rdr = new Query(BaseSchema).AddWhere(columnName, paramValue).ExecuteReader();
                if(rdr.Read())
                {
                    Load(rdr);
                }

                else
                {
                    //if no records, this is new still
                    MarkNew();
                }
            }
            finally
            {
                if(rdr != null)
                {
                    rdr.Close();
                }
            }
        }

        public void LoadByKey(object keyID)
        {
            MarkOld();
            IDataReader rdr = null;
            try
            {
                Query q = new Query(BaseSchema).AddWhere(BaseSchema.PrimaryKey.ColumnName, keyID);
                //CheckLogicalDelete(q);
                rdr = q.ExecuteReader();
                if(rdr.Read())
                {
                    Load(rdr);
                }
                else
                {
                    //if no records, this is new still
                    MarkNew();
                }
            }
            finally
            {
                if(rdr != null)
                {
                    rdr.Close();
                }
            }
        }

        #region Serializers

        public string ToXML()
        {
            Type type = typeof(T);
            XmlSerializer ser = new XmlSerializer(type);
            using(System.IO.MemoryStream stm = new System.IO.MemoryStream())
            {
                //serialize to a memory stream
                ser.Serialize(stm, this);

                //reset to beginning so we can read it.  
                stm.Position = 0;
                //Convert a string. 
                using(System.IO.StreamReader stmReader = new System.IO.StreamReader(stm))
                {
                    string xmlData = stmReader.ReadToEnd();
                    return xmlData;
                }
            }
        }

        /// <summary>
        /// Returns an object based on the passed-in XML.
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        public object NewFromXML(string xml)
        {
            object oOut = null;
            Type type = typeof(T);
            //hydrate based on private string var
            if(xml.Length > 0)
            {
                XmlSerializer serializer = new XmlSerializer(type);
                StringBuilder sb = new StringBuilder();
                sb.Append(xml);
                System.IO.StringReader sReader = new System.IO.StringReader(xml);
                oOut = serializer.Deserialize(sReader);
                //sb = null;
                sReader.Close();
            }

            return oOut;
        }

        #endregion

        #region CommandMethods

        public QueryCommand GetSelectCommand()
        {
            Query q = new Query(BaseSchema);
            q.QueryType = QueryType.Select;
            QueryCommand cmd = DataService.BuildCommand(q);
            return cmd;
        }

        #endregion

        #region Loaders

        protected void SetLoadState()
        {
            IsLoaded = true;
            IsNew = false;
            Loaded();
        }

        /// <summary>
        /// Loads the object with the current reader's values. Assumes the reader is already moved to
        /// first position in recordset (aka has been "Read()")
        /// </summary>
        /// <param name="rdr">The RDR.</param>
        public virtual void Load(IDataReader rdr)
        {
            
            foreach(TableSchema.TableColumn col in BaseSchema.Columns)
            {
                try
                {
                    SetColumnValue(col.ColumnName, rdr[col.ColumnName]);
                }
                catch
                {
                    throw new Exception("Unable to set column value for " + col.ColumnName);
                }
            }

            SetLoadState();
            MarkClean();
        }

        /// <summary>
        /// Loads the object with the current reader's values. Assumes the reader is already moved to 
        /// first position in recordset (aka has been "Read()")
        /// </summary>
        /// <param name="tbl"></param>
        public virtual void Load(DataTable tbl)
        {
            if(tbl.Rows.Count > 0)
            {
                DataRow dr = tbl.Rows[0];
                Load(dr);
            }
        }

        /// <summary>
        /// Loads the object with the current DataRow's values. 
        /// </summary>
        /// <param name="dr"></param>
        public virtual void Load(DataRow dr)
        {
            foreach(TableSchema.TableColumn col in BaseSchema.Columns)
            {
                try
                {
                    SetColumnValue(col.ColumnName, dr[col.ColumnName]);
                }
                catch(Exception x)
                {
                    //this will happen only if there's a reader error.
                    throw new Exception("Unable to set column value for " + col.ColumnName + "; " + x.Message);
                }
            }
            SetLoadState();
        }

        /// <summary>
        /// Opens the IDataReader, loads the object and closes the IDataReader. Unlike AbstractList.LoadAndCloseReader,
        /// this method does not assume that reader is open and in the first position!
        /// </summary>
        /// <param name="rdr"></param>
        public void LoadAndCloseReader(IDataReader rdr)
        {
            if(rdr.Read())
            {
                Load(rdr);
            }
            if(!rdr.IsClosed)
            {
                rdr.Close();
            }
        }

        #endregion

        #region Fetchers

        /// <summary>
        /// Returns all records for this table
        /// </summary>
        /// <returns>IDataReader</returns>
        public static IDataReader FetchAll()
        {
            //makes sure the table schema is loaded
            //new T();

            //build the query
            Query q = new Query(BaseSchema);
            CheckLogicalDelete(q);

            //load the reader
            IDataReader rdr = DataService.GetReader(q.BuildSelectCommand());
            return rdr;
        }

        /// <summary>
        /// Returns all records for this table, ordered
        /// </summary>
        /// <returns>Generic Typed List</returns>
        /// <param name="orderBy">Column to order by</param>
        public static IDataReader FetchAll(OrderBy orderBy)
        {
            //makes sure the table schema is loaded
            //new T();

            //build the query
            Query q = new Query(BaseSchema);
            CheckLogicalDelete(q);
            q.OrderBy = orderBy;

            //load the reader
            IDataReader rdr = DataService.GetReader(q.BuildSelectCommand());
            return rdr;
        }

        /// <summary>
        /// Returns all records for the given column/parameter, ordered by the passed in orderBy
        /// The expression for this is always "column=parameter"
        /// </summary>
        /// <param name="columnName">Name of the column to use in parmeter statement</param>
        /// <param name="oValue">Value of the column</param>
        /// <param name="oValue">Column to order by</param>
        /// <param name="orderBy">Ordering of results</param>
        /// <returns>IDataReader</returns>
        public static IDataReader FetchByParameter(string columnName, object oValue, OrderBy orderBy)
        {
            //makes sure the table schema is loaded
            //new T();
            //build the query
            Query q = new Query(BaseSchema);
            q.OrderBy = orderBy;
            CheckLogicalDelete(q); //CheckLogicalDelete Should Always Be Called Before AddWhere!
            q.AddWhere(columnName, oValue);

            //load the reader
            IDataReader rdr = DataService.GetReader(q.BuildSelectCommand());
            return rdr;
        }


        /// <summary>
        /// Returns all records for the given column/parameter
        /// The expression for this is always "column=parameter"
        /// </summary>
        /// <param name="columnName"></param>
        /// <param name="oValue"></param>
        /// <returns>IDataReader</returns>
        public static IDataReader FetchByParameter(string columnName, object oValue)
        {
            //new T();
            //build the query
            Query q = new Query(BaseSchema);
            CheckLogicalDelete(q); //CheckLogicalDelete Should Always Be Called Before AddWhere!
            q.AddWhere(columnName, oValue);
            

            //load the reader
            IDataReader rdr = DataService.GetReader(q.BuildSelectCommand());

            //load up the list
            return rdr;
        }

        /// <summary>
        /// Returns all records for the given column/parameter
        /// The expression for this is always "column=parameter"
        /// </summary>
        /// <param name="columnName"></param>
        /// <param name="comparison"></param>
        /// <param name="oValue"></param>
        /// <returns>IDataReader</returns>
        public static IDataReader FetchByParameter(string columnName, Comparison comparison, object oValue)
        {
            //new T();
            //build the query
            Query q = new Query(BaseSchema);
            CheckLogicalDelete(q); //CheckLogicalDelete Should Always Be Called Before AddWhere!
            q.AddWhere(columnName, comparison, oValue);

            //load the reader
            IDataReader rdr = DataService.GetReader(q.BuildSelectCommand());
            return rdr;
        }

        /// <summary>
        /// Returns all records for the given column/parameter
        /// The expression for this is always "column=parameter"
        /// </summary>
        /// <param name="columnName"></param>
        /// <param name="comparison"></param>
        /// <param name="oValue"></param>
        /// <param name="orderBy"></param>
        /// <returns>IDataReader</returns>
        public static IDataReader FetchByParameter(string columnName, Comparison comparison, object oValue, OrderBy orderBy)
        {
            //new T();
            //build the query
            Query q = new Query(BaseSchema);
            CheckLogicalDelete(q); //CheckLogicalDelete Should Always Be Called Before AddWhere!
            q.AddWhere(columnName, comparison, oValue);
            q.OrderBy = orderBy;
            //load the reader
            IDataReader rdr = DataService.GetReader(q.BuildSelectCommand());

            //load up the list
            return rdr;
        }

        /// <summary>
        /// Returns all records for the given query
        /// </summary>
        /// <returns>Generic Typed List</returns>
        /// <param name="query">Query for complex records</param>
        public static IDataReader FetchByQuery(Query query)
        {
            //makes sure the table schema is loaded
            //new T();
            CheckLogicalDelete(query);

            //load the reader
            IDataReader rdr = DataService.GetReader(query.BuildSelectCommand());
            return rdr;
        }

        /// <summary>
        /// Uses the passed-in object as a parameter set. Does not use the created/modified fields
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public static IDataReader Find(T item)
        {
            return Find(item, null);
        }

        public static IDataReader Find(T item, OrderBy orderBy)
        {
            //build the sql string and command statements at the same time
            Query q = new Query(BaseSchema);
            CheckLogicalDelete(q);

            //retrieve data from database

            foreach(TableSchema.TableColumn col in BaseSchema.Columns)
            {
                string columnName;
                object columnValue;
                columnName = col.ColumnName;
                columnValue = item.GetColumnValue<object>(columnName);
                if(!Utility.IsAuditField(columnName))
                {
                    object defaultValue = String.Empty;
                    switch(col.DataType)
                    {
                        case DbType.Boolean:
                            defaultValue = false;
                            break;
                        case DbType.Currency:
                        case DbType.Decimal:
                        case DbType.Int16:
                        case DbType.Double:
                        case DbType.Int32:
                            defaultValue = 0;
                            break;
                        case DbType.Date:
                        case DbType.DateTime:
                            defaultValue = new DateTime(1900, 1, 1);
                            break;
                        case DbType.Guid:
                            defaultValue = Guid.Empty;
                            break;
                    }
                    if(columnValue != null)
                    {
                        if(!columnValue.Equals(defaultValue))
                        {
                            q.AddWhere(columnName, columnValue);
                        }
                    }
                }
            }

            if(orderBy != null)
            {
                q.OrderBy = orderBy;
            }
            IDataReader rdr = DataService.GetReader(q.BuildSelectCommand());
            return rdr;
        }

        #endregion

        #region Utility

        /// <summary>
        /// Returns an ordered ListItemCollection for use with DropDowns, RadioButtonLists, and CheckBoxLists
        /// Note: This method assumes that the column in the second position is the text value column
        /// </summary>
        /// <returns></returns>
        public static ListItemCollection GetListItems()
        {
            //get the textColumn based on position
            //which should be the second column of the table
            //T item = new T();
            string textColumn = BaseSchema.Columns[1].ColumnName;
            return GetListItems(textColumn);
        }

        /// <summary>
        /// Returns an ordered ListItemCollection for use with DropDowns, RadioButtonLists, and CheckBoxLists
        /// </summary>
        /// <param name="textColumn">The name of the column which should be used as the text value column</param>
        /// <returns></returns>
        public static ListItemCollection GetListItems(string textColumn)
        {
            //T item = new T();
            ListItemCollection list = new ListItemCollection();
            string pkCol = BaseSchema.PrimaryKey.ColumnName;
            string textCol = BaseSchema.GetColumn(textColumn).ColumnName;

            //run a query retrieving the two columns
            Query q = new Query(BaseSchema);
            q.SelectList = pkCol + "," + textCol;
            q.OrderBy = OrderBy.Asc(textCol);
            IDataReader rdr = q.ExecuteReader();

            while(rdr.Read())
            {
                ListItem listItem = new ListItem(rdr[1].ToString(), rdr[0].ToString());
                list.Add(listItem);
            }
            return list;
        }

        /// <summary>
        /// If this object has a logical delete column, this method will append in the required parameter to avoid returning 
        /// deleted records
        /// </summary>
        /// <param name="q"></param>
        internal static void CheckLogicalDelete(Query q)
        {
            //check the columns and see if there's a "deleted" or "isDeleted"
            q.CheckLogicalDelete();
        }

        public TableSchema.TableColumnSettingCollection GetColumnSettings()
        {
            return columnSettings;
        }

        /// <summary>
        /// Copies the current instance to a new instance
        /// </summary>
        /// <returns>New instance of current object</returns>
        public T Clone()
        {
            //copy this instance to a new instance
            T thisInstance = new T();

            foreach(TableSchema.TableColumnSetting setting in columnSettings)
            {
                thisInstance.SetColumnValue(setting.ColumnName, setting.CurrentValue);
            }
            return thisInstance;
        }

        /// <summary>
        /// Copies current instance to passed in instance
        /// </summary>
        /// <param name="copyInstance"></param>
        public void CopyTo(T copyInstance)
        {
            if(copyInstance == null)
            {
                copyInstance = new T();
            }
            foreach(TableSchema.TableColumnSetting setting in columnSettings)
            {
                copyInstance.SetColumnValue(setting.ColumnName, setting.CurrentValue);
            }
        }

        /// <summary>
        /// Adds a new row to a Datatable with this record
        /// You must be sure the column names are the same
        /// </summary>
        /// <param name="dataTable"></param>
        public void CopyTo(DataTable dataTable)
        {
            DataRow newRow = dataTable.NewRow();
            foreach(TableSchema.TableColumnSetting setting in columnSettings)
            {
                try
                {
                    newRow[setting.ColumnName] = setting.CurrentValue;
                }
                catch
                {
                    //swallow this - this is a forgiving procedure :)
                }
            }
            dataTable.Rows.Add(newRow);
        }

        /// <summary>
        /// Copies a record from a DataTable to this instance. Column names must match.
        /// </summary>
        /// <param name="row"></param>
        public void CopyFrom(DataRow row)
        {
            foreach(TableSchema.TableColumnSetting setting in columnSettings)
            {
                try
                {
                    setting.CurrentValue = row[setting.ColumnName];
                }
                catch
                {
                    //swallow this - this is a forgiving procedure :)
                }
            }
        }

        /// <summary>
        /// Copies the passed-in instance settings to this instance
        /// </summary>
        /// <param name="copyInstance"></param>
        public void CopyFrom(T copyInstance)
        {
            if(copyInstance != null)
            {
                foreach(TableSchema.TableColumnSetting setting in copyInstance.columnSettings)
                {
                    SetColumnValue(setting.ColumnName, setting.CurrentValue);
                }
            }
            else
            {
                throw new Exception("Copy instance is null");
            }
        }

        #endregion

        #region WebUI Helper

        private string nullExceptionMessage = "{0} requires a value";
        [XmlIgnore]
        [HiddenForDataBinding(true)]
        public string NullExceptionMessage
        {
            get { return nullExceptionMessage; }
            protected set { nullExceptionMessage = value; }
        }

        private string invalidTypeExceptionMessage = "{0} is not a valid {1}";
        [XmlIgnore]
        [HiddenForDataBinding(true)]
        public string InvalidTypeExceptionMessage
        {
            get { return invalidTypeExceptionMessage; }
            protected set { invalidTypeExceptionMessage = value; }
        }

        private string lengthExceptionMessage = "{0} exceeds the maximum length of {1}";
        [XmlIgnore]
        [HiddenForDataBinding(true)]
        public string LengthExceptionMessage
        {
            get { return lengthExceptionMessage; }
            protected set { lengthExceptionMessage = value; }
        }

        private System.Collections.Generic.List<string> errorList = new System.Collections.Generic.List<string>();
        [HiddenForDataBinding(true)]
        protected System.Collections.Generic.List<string> Errors
        {
            get { return errorList; }
        }

        public bool HasErrors() {
            return errorList.Count > 0;
        }

        /// <summary>
        /// Loops the underlying settings collection to validate type, nullability, and length
        /// </summary>
        public void ValidateColumnSettings()
        {
            //loop the current settings
            //make sure they are valid for their type
            foreach(TableSchema.TableColumnSetting setting in GetColumnSettings())
            {
                //Utility.WriteTrace("Validating " + setting.ColumnName);
                object settingValue = setting.CurrentValue;
                TableSchema.TableColumn col = table.GetColumn(setting.ColumnName);

                if(!col.IsReadOnly)
                {
                    string formattedName = Utility.ParseCamelToProper(col.ColumnName);
                    Type t = col.GetPropertyType();

                    //Convert the existing value to the type for this column
                    //if there's an error, report it.
                    //OK to bypass if the column is nullable and this setting is null
                    //just check for now if the value isn't null - it will be checked
                    //later for nullability

                    if(!col.IsNullable && settingValue != null && settingValue != DBNull.Value)
                    {
                        try
                        {
                            if(col.DataType != DbType.Guid)
                            {
                                Convert.ChangeType(settingValue, t);
                            }
                        }
                        catch
                        {
                            //there's a conversion problem here
                            //add it to the Exception List<>
                            if(col.IsNumeric)
                            {
                                errorList.Add(String.Format(InvalidTypeExceptionMessage, formattedName, "number"));
                            }
                            else if(col.IsDateTime)
                            {
                                errorList.Add(String.Format(InvalidTypeExceptionMessage, formattedName, "date"));
                            }
                            else
                            {
                                errorList.Add(String.Format(InvalidTypeExceptionMessage, formattedName, "value"));
                            }
                        }
                    }
                    //now make sure that this column's null settings match with what's in the setting
                    //Utility.WriteTrace("Testing nullability of " + setting.ColumnName);
                    if(!col.IsNullable && (settingValue == null || settingValue == DBNull.Value))
                    {
                        //Utility.WriteTrace("Null Error Caught " + setting.ColumnName);
                        errorList.Add(String.Format(NullExceptionMessage, formattedName));
                    }


                    //finally, check the length
                    //Utility.WriteTrace("Testing Max Length of " + setting.ColumnName);
                    if((settingValue != null && settingValue != DBNull.Value) && col.MaxLength > 0)
                    {
                        if(col.DataType != DbType.Boolean && settingValue.ToString().Length > col.MaxLength)
                        {
                            //Utility.WriteTrace("Max Length Exceeded " + col.ColumnName + " can't exceed " + col.MaxLength + "; current value is set to " + settingValue.ToString().Length);
                            errorList.Add(String.Format(LengthExceptionMessage, formattedName, col.MaxLength));
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Loads your object from a form postback
        /// </summary>
        public void LoadFromPost()
        {
            LoadFromPost(true);
        }

        /// <summary>
        /// Loads your object from a form postback
        /// </summary>
        /// <param name="validatePost">Set this to false to skip validation</param>
        public void LoadFromPost(bool validatePost)
        {
            if(System.Web.HttpContext.Current != null)
            {
                //use Request.form, since the ControlCollection can return weird results based on 
                //the container structure.
                System.Collections.Specialized.NameValueCollection formPost = System.Web.HttpContext.Current.Request.Form;
                TableSchema.TableColumnSettingCollection settings = GetColumnSettings();

                if(formPost != null && settings != null)
                {
                    foreach(string s in formPost.AllKeys)
                    {
                        //Utility.WriteTrace("Looking at form field " + s);

                        foreach(TableSchema.TableColumnSetting setting in settings)
                        {
                            if(s.ToLower().EndsWith("_" + setting.ColumnName.ToLower()) || s.ToLower().EndsWith("$" + setting.ColumnName.ToLower()) || s.ToLower().Equals(setting.ColumnName.ToLower()))
                            {
                                setting.CurrentValue = formPost[s];
                                //Utility.WriteTrace("Matched " + s + " to " + setting.ColumnName);
                            }
                        }
                    }
                }
                //validate the settings, since we're setting the object values here, not
                //using the accessors as we should be.
                if(validatePost)
                {
                    ValidateColumnSettings();

                    if(errorList.Count > 0)
                    {
                        //format this for the web
                        if(System.Web.HttpContext.Current != null)
                        {
                            //decorate the output
                            string errorReport = "<b>Validation Error:<b><ul>";
                            foreach(string s in errorList)
                            {
                                errorReport += "<li><i>" + s + "</i></li>";
                            }
                            errorReport += "</ul>";
                            throw new Exception(errorReport);
                        }
                        else
                        {
                            throw new Exception("Validation error - catch this and check the ExceptionList property to review the exceptions. You can change the output message as needed by accessing the ExceptionMessage properties of this object");
                        }
                    }
                }
            }
        }

        #endregion
    }

    [AttributeUsage(AttributeTargets.Property)]
    sealed class HiddenForDataBindingAttribute : Attribute
    {
        private bool _isHidden;
        public HiddenForDataBindingAttribute()
        {
        }

        public HiddenForDataBindingAttribute(bool isHidden)
        {
            _isHidden = isHidden;
        }

        public bool IsHidden
        {
            get { return _isHidden; }
        }
    }
}

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)

Share

About the Author

Alex Mueller
Web Developer
United States United States
No Biography provided

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150327.1 | Last Updated 17 Nov 2007
Article Copyright 2007 by Alex Mueller
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid