rioting bits

jedes herz ist eine revolutionäre zelle

Archive for March, 2009

A simple way to persist the Linq-to-SQL Data Context

March 16th, 2009

The 3.0 version of C# introduced Linq-to-SQL, an Object-Relational Mapping tool that allows querying relational data using .Net classes. Linq-to-SQL uses a class named DataContext to keep track of the mapping objects and secure their persistance.

But, if you stumbled-upon this post, you probably know these by now. I should add a small explanation by Rick Strahl about the behaviour of the DataContext:

Linq to SQL has a persistent approach to managing its ‘connection’ to the database via this data context and it basically assumes that you use a single DataContext to make all of your data related access. This doesn’t mean that it makes persistent connections to the database, but means that the DataContext instance maintains state about active result sets, which is especially important if change tracking is on which is the default.

Now, Linq-to-SQL, especially with its language enhancements, greatly improves the handling of database data, making the code more fast-to-produce, maintainable and most of all readable. The classes included in Linq’s standard libraries are enough for most of small-scale project architectures.

However, when trying to develop a multi-tired web application, most people get to problems where their objects get detached from the DataContext and awkward ways are used to track their changes. The scenario is as follows: the object is retrieved from the DataContext, some changes are made to it, the object is passed through different layers of abstraction in the project architecture (and possibly a postback or two) and finally you end up with a detached object. There are several ways of attaching it back, but they either imply some changes to the table mapped by the object, either some cumbersome code that you have to repeat each time you have the problem.

The solution I’m using is one of the options Rick Strahl talks about in his article, creating a thread-specific DataContext, but using the CallContext to make the DataContext persistent, and in rather few lines of code. The basic idea is keeping the same DataContext during a given session, which will be the only one responsible for keeping track of your objects and thus avoiding them to get detached.

Enough with the talk. The code now. Basically it revolves around using a DataContext Factory to keep your DataContext in the CallContext:

using System.Data.Linq;
using System.Runtime.Remoting.Messaging;

namespace RiotingBits.Data
{
    public class DataContextFactory<T> where T : DataContext, new()
    {
        public static T RetrieveDataContext()
        {
            string key = string.Format("DataContext_{0}", typeof(T));

            T currentDataContext = (T)CallContext.GetData(key);
            if (currentDataContext == null)
            {
                currentDataContext = new T();
                CallContext.SetData(key, currentDataContext);
            }

            return currentDataContext;
        }
    }
}

As you can see, the Factory works with generic DataContext types, to allow its usage with different data contexts you may have in your application. The next step is creating an extender for the DataContext you want to persist:

namespace RiotingBits.Data
{
    public class DataContextExtender : DataContextFactory<RiotingBitsDataContext>
    {
        public static RiotingBitsDataContext DataContext
        {
            get
            {
                return RetrieveDataContext();
            }
        }
    }
}

Here, RiotingBitsDataContext is the name I gave to my DataContext.

After adding these two classes, you don’t even have to instantiate your DataContext each time you use it. You simply use the static property of the Extender. Some examples of usage for Read/Update/Insert on a normal Users table:

public static User GetById(int userId)
{
    return DataContextExtender.DataContext.Users.Where(u => u.Id == userId).SingleOrDefault();
}

public void Save()
{
    if (this.Id == 0)
        DataContextExtender.DataContext.Users.InsertOnSubmit(this);

    DataContextExtender.DataContext.SubmitChanges();
}

I made a small project to demonstrate a complete working example of this solution. You can download it from here.

As a closing-paragraph, I’d like to debate about the necessity of such kind of a solution. Is it something terribly wrong with Linq-to-SQL that you may need this? I tend to say no. When you have complex architectures, this is a thing that’s bound to happen, making your own custom classes to address your project’s architectural needs. Linq-to-SQL behaves exactly as an Object-Relational Mapping tool is expected to. So my thoughts go more on the line of Ian Cooper’s:

I read a fair number of opinions that are suprised by the behavior of LINQ To SQL. However, having used ORMs for a number of years, I find LINQ To SQL conforms to my expectations as to how an ORM should behave. Indeed a reading of something as old now as Martin Fowler’s Patterns of Enteprise Application Architecture and you will find exactly this pattern of behavior for an ORM discussed. Indeed WORM (Wilson O/R Mapper now open source BTW) and NHibernate both behave in a similar fashion. So some of this seems to be based on expectations that don’t come from experience of using ORM tools.