Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to improve performance of typed DataSet creation

0.00/5 (No votes)
23 May 2005 1  
How to improve performance of typed DataSet creation.

Introduction

A common object used to be passed in N-tier applications in .NET environment is a DataSet object. Moreover in order to ease the use of the DataSet object, we usually define it as a strongly typed DataSet (using XML schema).

A common scenario is to create (instantiate) the typed DataSet in one tier and then to pass it to the other tiers for further logic implementations. The creation of a typed DataSet is actually very expensive. I was amazed to realize (using various profiling tools) that around 15%-20% of my application was wasted on typed DataSets' ctors.

A proposed solution

Most of the applications use the following flow: a newly created DataSet is populated with data (either by the user or from the DB) updated with some logic, saved to DB and then finally discarded. The cycle then repeats itself.

(Refer the Remarks section for additional information of what can be done in cases where more than one instance of such a DataSet is needed.)

If the DataSet would be created only once, it would improve the performance significantly. Therefore the proposed solution is as follows: create once the desired typed DataSet, save it to a cache, and once needed, return it just after clearing its data using the DataSet.Clear() method. Is it essentially faster? Yes, I have tested it using various profilers and the DataSet.Clear() method is faster by 2-10 (!!!) times than a typed DataSet's ctor.

Below is the source code for a typed DataSet proxy which controls the creation of the desired typed DataSet.

namespace TypedDSProxy
{
    ///<summary>

    /// Controls creation of typed DataSet.

    /// Singleton.

    ///</summary>

    public class DSProxy
    {
        ///<summary>

        /// Initial size of typed DS cache.

        ///</summary>

        private const int InitialCacheSize = 2;
        ///<summary>

        /// Typed DS cache. 

        ///</summary>

        private Hashtable cache;

        ///<summary>

        /// Instance variable for Singleton pattern.

        ///</summary>

        private static DSProxy DSProxyInstance;

        ///<summary>

        /// Default Constructor.

        ///</summary>

        private DSProxy()
        {
            cache = new Hashtable(InitialCacheSize);
        }
        ///<summary>

        /// Instance method for Singleton pattern.

        ///</summary>

        ///<returns>DSProxy</returns>

        public static DSProxy Instance()
        {
            if(DSProxyInstance == null)
                DSProxyInstance = new DSProxy();
            return DSProxyInstance;
        }

        ///<summary>

        ///    Gets the namespace from the given type.

        ///(The namespace derived from the beginning

        ///      of the type till the first "." char.)

        ///</summary>

        ///<param name="dsType">The string representation

        ///                  of typed DS's type.</param>

        ///<returns>Typed DS's namespace.</returns>

        private string GetNamespace(string dsType)
        {
            try
            {
                return dsType.Substring(0, dsType.IndexOf("."));
            }
            catch(Exception e)
            {
                // write e.Message to log.

                ...
            }
        }

        ///<summary>

        /// Returns an empty typed DataSet according to a given type.

        ///</summary>

        ///<param name="dsType">The string representation

        ///       of typed DS's type.</param>

        ///<returns>Empty typed DS.</returns>

        public DataSet GetDS(string dsType)
        {
            try
            {
                DataSet ds;
                // if the DataSet wasn't created.

                if(cache[dsType] == null)
                {
                    // create it using its assembly.

                    Assembly asm = Assembly.Load(GetNamespace(dsType));
                    ds = (DataSet)asm.CreateInstance(dsType, true);
                    cache.Add(dsType, ds);
                }
                else
                {
                    // else clear it.

                    ds = (DataSet)cache[dsType];
                    ds.Clear();
                }
                return ds;

            }
            catch(Exception e)
            {
                // write e.Message to log.

                ...
            }
        }
    }
}

The client uses the typed DataSet proxy as follows:

class clsClient
{
    ...
    
    public void SomeOperationWithTypedDS()
    {
        OrdersDS ds = 
          (OrdersDS)DSProxy.Instance().GetDS(typeof(OrdersDS).ToString());
        ds.Orders.AddOrdersRow("a", 1, System.DateTime.Now, 
           System.DateTime.Now, System.DateTime.Now, 1, 
           decimal.MinValue, "a", "a", "a", "a", "a", "a");
    }

    ...
}

Remarks

  • The proposed solution is not thread safe. If thread safety is an issue then a pool of typed DataSets can be used. (A pool of objects also solves cases where more than one typed DataSet object is needed.) A good article about managed pools of objects can be found here: Object Pooling with Managed Code - By Scott Sherer. (When thread safety is not an issue and still more than one typed DataSet object is needed, a simpler pool - without object context - can be used.)
  • Reflection is used here in order to avoid switching while creating the desired typed DataSet�s type.

    Another approach is to use the ConstructorInfo object which can be extracted from the DataSet�s Type in order to create the instance. Use ConstructorInfo.Invoke() method in order to accomplish the task.

  • Any suggestions, improvements and bug reports will be very much appreciated.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here