Click here to Skip to main content
15,893,487 members
Articles / Programming Languages / C#

Generic Multi-Field/Property Sorting for Lists of Business Objects

Rate me:
Please Sign up or sign in to vote.
4.89/5 (13 votes)
13 Feb 2008CPOL8 min read 75.7K   496   72  
This article presents a simple and flexible way to sort strongly-typed lists of business objects using multiple properties or fields.
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;

// Source code by Owen Emlen (owene_1998@yahoo.com, owen@binarynorthwest.com)

namespace MultiSortDemo
{
    /// <summary>
    /// Stores a "work item" pseudo-business object.  
    /// Lists of work items are sorted to test multi-property sort speed in this demo.
    /// Note the addition of Marc Gravell's HyperTypeDescriptionProvider attribute.  This greatly speeds property
    /// value retrieval speed.  If you like, just for kicks, try removing it and note the relative performance :P
    /// </summary>
    [TypeDescriptionProvider(typeof(Hyper.ComponentModel.HyperTypeDescriptionProvider))]
    public class WorkItem
    {
        public WorkItem() { dtAssigned = DateTime.MinValue; }

        /// <summary>
        /// Stores a mapping between a property name and the underlying field that stores the
        /// property's value.  Could be generated dynamically.
        /// </summary>
        public static PropertyFieldMappings<WorkItem> htPropertyToFieldNameMapping =
          new PropertyFieldMappings<WorkItem>();

        // IMPORTANT NOTE: if we access PROTECTED or PRIVATE *fields*, security is 
        // checked on each value retrieval, slowing everything down.

        // let's make our fields public for demonstration purposes
        public bool fItemFinished;
        public DateTime dtAssigned;
        public DateTime dtItemFinished;
        public string sAssignedTo;
        public string sAssignedBy;
        public int nPriority;

        // Let's throw in an enum field to demonstrate how enums can be sorted by their string representations
        public BoredomRating nTaskBoredom;

        /// <summary>
        /// Gets or sets to whom this work item is assigned
        /// </summary>
        [PropertyFieldName("sAssignedTo")]
        public string AssignedTo
        {
            get { return (sAssignedTo == null) ? "(Unassigned)" : sAssignedTo; }
            set
            {
                if (value == null) sAssignedTo = null;
                else
                {
                    if (sAssignedTo == null)
                    {
                        // new assignment, record the date/time assigned
                        sAssignedTo = value;
                        dtAssigned = DateTime.Now.Date;
                    }
                    else
                    {
                        sAssignedTo = value;
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets who assigned this work item
        /// </summary>
        [PropertyFieldName("sAssignedBy")]
        public string AssignedBy
        {
            get { return (sAssignedBy == null) ? "(Management)" : sAssignedBy; }
            set { sAssignedBy = value; }
        }

        /// <summary>
        /// Gets or sets the date/time at which this work item was assigned
        /// Default to 1/1/2000 if the work item was never formally assigned
        /// </summary>
        [PropertyFieldName("dtAssigned")]
        public DateTime DateAssigned
        {
            get { return (dtAssigned == DateTime.MinValue) ? new DateTime(2000, 1, 1, 1, 1, 1) : dtAssigned; }
            set { dtAssigned = value; }
        }

        /// <summary>
        /// Gets or sets the priority of the work item
        /// </summary>
        [PropertyFieldName("nPriority")]
        public int Priority
        {
            get { return nPriority; }
            set { nPriority = value; }
        }

        /// <summary>
        /// Gets or sets the anticipated task 'interest' level
        /// </summary>
        [PropertyFieldName("nTaskBoredom")]
        public BoredomRating TaskBoredom
        {
            get { return nTaskBoredom; }
            set
            {
                //if (value == BoredomRating.EvenWorseThanCodingASPWebPages)
                //{
                //    Debug.WriteLine("Stop complaining and get back to work!");
                //    CompanySecurity.ReportSuspiciousActivity("Employee is bored.  Assign more work and dock pay.");
                //}
                nTaskBoredom = value;
            }
        }

        /// <summary>
        /// Gets or sets whether the item has been finished or completed.  
        /// If switching from unfinished to finished, the date/time of completion is recorded.
        /// </summary>
        [PropertyFieldName("fItemFinished")]
        public bool ItemFinished
        {
            get { return fItemFinished; }
            set
            {
                // Completing an unfinished item, record the date/time
                if (!fItemFinished && value == true)
                {
                    dtItemFinished = DateTime.Now.Date;
                    fItemFinished = true;
                }
            }
        }

        // Return the time the item was completed/finished.  If the work item has not
        // been completed, return DateTime.MinValue
        [PropertyFieldName("dtItemFinished")]
        public DateTime DateFinished
        {
            get { return (fItemFinished ? dtItemFinished : DateTime.MinValue); }
        }

        /// <summary>
        /// Method used to alter the work item's assigned date
        /// </summary>
        /// <param name="nDays"></param>
        public void FoundOutAboutWorkItemDaysLater(int nDays)
        {
            dtAssigned = dtAssigned.AddDays(nDays);
        }

        /// <summary>
        /// Method to report the work item was completed several days ago
        /// </summary>
        /// <param name="nDays"></param>
        public void PretendCompleted(int nDays)
        {
            fItemFinished = true;
            dtItemFinished = DateTime.Now.Date.AddDays(-nDays);
        }

        /// <summary>
        /// This would be best done with Attributes on properties, but for demonstration
        /// purposes, the property<->field name link will be hardcoded
        /// </summary>
        public static void SetupQuickAndDirtyPropertyToFieldLinkLookup()
        {
            // In the real world we simply place attributes on the properties when a
            // quick-lookup-by-field is permitted.  Since this is a quick demo, I'm
            // hardcoding the mapping table below...
            // htPropertyToFieldNameMapping.Add("ItemFinished", "fItemFinished");
            // htPropertyToFieldNameMapping.Add("DateFinished", "dtItemFinished");
            // htPropertyToFieldNameMapping.Add("TaskBoredom", "nTaskBoredom");
            // htPropertyToFieldNameMapping.Add("Priority", "nPriority");
            // htPropertyToFieldNameMapping.Add("DateAssigned", "dtAssigned");
            // htPropertyToFieldNameMapping.Add("AssignedTo", "sAssignedTo");
            // htPropertyToFieldNameMapping.Add("AssignedBy", "sAssignedBy");
        }
    }

    /// <summary>
    /// Encapsulates a Work Item property name for easy data binding to UI/controls
    /// </summary>
    public class WorkItemPropertyName
    {
        private string sPropName;
        public string PropertyName { get { return sPropName; } set { sPropName = value; } }
        public WorkItemPropertyName(string sPropertyName) { sPropName = sPropertyName; }
    }

    /// <summary>
    /// Dummy ratings for work items.  Used to demonstrate sorting via enum values
    /// Not placed within the work item class because this may also be used to rate
    /// company social events...
    /// </summary>
    public enum BoredomRating 
    {
        NoRating = 0,
        EvenWorseThanCodingASPWebPages = 1,
        TediusAndDull = 2,
        BarelyTolerable = 3,
        Tolerable = 4,
        SomewhatFun = 5,
        DownrightExciting = 6,
        MaxExcitement = 7
    }
}

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
Software Developer (Senior) Troppus Software
United States United States
Currently working as a Senior Silverlight Developer with Troppus Software in Superior, CO. I enjoy statistics, programming, new technology, playing the cello, and reading codeproject articles. Smile | :)

Comments and Discussions