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 (6)
  1. Alex says:

    Can you please prove that perfomance is increased?

    I can’t see any changes/

  2. Mohammad Gabr says:

    Good idea , but why shouldn’t i cache the DataContext Object itself

  3. Stuart Cam says:

    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

  4. Rick says:

    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.

  5. Kevin says:

    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.

  6. James says:

    "I believe that the DataContext is already caching its MappingSource via the static property."

    Agree – I've found this does nothing.

Comments are closed.

Skip to main content