Dela via


LINQ to SQL: Optimizing DataContext construction with the factory pattern

In the context of building out a web application, the corresponding DataContext is meant to be built out several times. The application's DataContext is constructed, some sort of data retrieval or manipulation is done, and then the DataContext goes out of scope. This operation will most likely be done several, several times throughout common user flows.

However, constructing a DataContext object is a relatively expensive operation. 99.9% of this performance hit is spent building out the MappingSource object that gets used by your DataContext. Consider that, in a stock scenario, your application goes out and builds out the MappingSource object every single time that the DataContext object is created. Typically, this is something that will happen very frequently.

These calls can be significantly optimized by caching the expensive part of the operation, which is the construction of the MappingSource object. If we wrap our DataContext construction around a factory method, we have a nice, centralized point for making this happen.

Consider the following code example:

    public static class MyDataContextFactory

    {

        private static MappingSource _myMappingSource;

        public static MyDataContext Get()

        {

            MyDataContext ctx;

            if (_myMappingSource == null)

            {

                ctx = new MyDataContext();

                _myMappingSource = ctx.Mapping.MappingSource;

            }

           else

            {

                ctx = new MyDataContext(_myMappingSource);

            }

            return ctx;

        }

    }

This example caches the MappingSource object by storing it in a static variable. We have another source file containing a partial class of MyDataContext. This partial class adds a new constructor that takes in a MappingSource object.

This partial class is used to ensure that calling code doesn't have to worry about where the connection string comes from when passing a MappingSource into the constructor of a DataContext object. Our partial class now looks something like this:

public partial class MyDataContext {     public MyDataContext(MappingSource mappingSource)         : this(Settings.Default.MyConnectionString, mappingSource)     {     } }

Now, our DataContext construction is exceptionally cheap after the first call. All subsequent calls will gain the performance advantage of the cached MappingSource object. In some informal performance testing, we found this approach to be a significant reduction in the amount of time it takes to construct a DataContext.

Comments

  • Anonymous
    June 17, 2008
    Can you please prove that perfomance is increased? I can't see any changes/

  • Anonymous
    August 07, 2008
    Good idea , but why shouldn't i cache the DataContext Object itself

  • Anonymous
    August 18, 2008
    Look in your generated code: private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource(); The mappingSource is already marked as static... ...therefore I am having trouble understanding what you have gained by moving it out! Stuart

  • Anonymous
    August 19, 2009
    Stuart, the difference is that before, MyDataContext() was created every time Get() was called. Now it only gets called once the first time. Before, calling Get() was quite expensive because each time it had to create a new MyDataContext(). When this gets called lots of times it is very expensive on resources.

  • Anonymous
    October 07, 2009
    I ran some tests and found that this method is not necessary. After looking at Stuart's comment, I believe that the DataContext is already caching its MappingSource via the static property. As long as your program lives in memory, the MappingSource will be cached. However, the VERY FIRST TIME you instantiate the data context in your program, I bet the performance hit is fairly substantial (I don't have the data to back this theory up). Here are the results of my test: Each data context was instantiated 1 million times. The time reported is the total time of the loop. Small data context: 00:00:39.4218750 Small data context with mappingSource caching: 00:00:39.3437500 Big data context: 00:00:39.9375000 Big data context with mappingSource caching: 00:00:39.7656250 As you can see, there's only about a 0.5 second variance between all of the tests, which shows 2 things:

  1. Caching of the mapping source is not necessary
  2. Number of entities in the data model doesn't have much effect on instantiation over time (although it may possibly effect the first instantiation) Hope this helps.
  • Anonymous
    November 02, 2010
    "I believe that the DataContext is already caching its MappingSource via the static property." Agree - I've found this does nothing.