SQL CE 3.5 with LINQ to SQL

See updated post: SQL CE 3.5 with LINQ to SQL Revisited

Using LINQ to SQL with SQL CE 3.5 can be a bit of a challenge.  First off, the LINQ to SQL Visual Studio designer doesn’t support SQL CE so you need to run sqlmetal from the command line to create the object model (or write it by hand).  Once you get past this point then you can use LINQ to SQL the same way you would for SQL Sever BUT there is a catch.

The way LINQ to SQL is built makes it work well with SQL Server and its connection pooling ability.  If you look at this FAQ under the “Database Connection: Open How Long?” section it says:

A connection typically remains open until you consume the query results.

Therefore given the following code:

using(Northwind db = new Northwind(MyConnectionString))
{
    (from p in db.Products).ToList();
    (from p in db.Customers).ToList();
}

On line 3 and 4 in the above code will open a database connection (which will be pulled from the open connections in connection pool), execute the query, and then close the connection after the operation is completed (which will return the connection to the pool).

This works great with connection pooling but when you move to SQL CE you don’t have that luxury.  What happens now is that for each query a new SQL CE connection will be opened, the query will be executed and then the connection is closed.  Each query is incurring the cost of opening a new connection.  To make matters even worse, in SQL CE the first open connection  brings the database engine into memory, and once you have no open connections anymore it removes the engine from memory.  What this means is that each time we close the only open connection and then re-open we are incurring a HUGE cost.

One way to get around this is to pass into the DataContext an open SQLCeConnection object.  LINQ to SQL will only automatically close a connection if it opens it.  Therefore, if you pass it an open connection then you won’t incur this cost over and over again. This will work fine in a single threaded application but once you move to a mutlithreaded app where you are performing database operations from different threads you encounter a problem: The SQLCeConnection object is not thread safe.  You need to have a different connection object per thread in order to make this work.  What you want is to be able to request a connection from any thread and get an already opened one for that thread.  This sounds a lot like a simple connection pooler, which could look something like this:

/// <summary>
/// Manages open connections on a per-thread basis
/// </summary>
public class ConnectionPool
{
    private Dictionary<int, IDbConnection> threadConnectionMap;

    /// <summary>
    /// Gets the connection string.
    /// </summary>
    /// <value>The connection string.</value>
    public string ConnectionString
    {
        get;
        private set;
    }

    /// <summary>
    /// Gets a connection.
    /// </summary>
    /// <returns>An open connection</returns>
    public IDbConnection Connection
    {
        get
        {
            lock (threadConnectionMap)
            {
                int threadId = Threading.Thread.CurrentThread.ManagedThreadId;

                IDbConnection connection = null;
                if (threadConnectionMap.ContainsKey(threadId))
                {
                    connection = threadConnectionMap[threadId];
                }
                else
                {
                    connection = new SqlCeConnection(ConnectionString);
                    connection.Open();
                    threadConnectionMap.Add(threadId, connection);
                }

                return connection;
            }
        }
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="ConnectionPool"/> class.
    /// </summary>
    /// <param name="connectionString">The connection string.</param>
    public ConnectionPool(string connectionString)
    {
        threadConnectionMap = new Dictionary<int, IDbConnection>();
        ConnectionString = connectionString;
    }

With this you create a DataContext like this:

// Defined somewhere
ConnectionPool connectionPool = new ConnectionPool(MyConnectionString);

// ...
// ...

using(Northwind db = new Northwind(connectionPool.Connection))
{
    (from p in db.Products).ToList();
    (from p in db.Customers).ToList();
}

Now each thread will have its own open connection which will minimize the cost of opening connections.