SqlCommand Parameter Caching

Last week, I wrote a short article about the SqlCommandBuilder.DeriveParameters method that can be used to automatically populate the parameters of a stored procedure command. One of the downsides to this approach is that each call to the method will result in a call to the database. Since stored procedure parameters aren’t typically changing very often, this can result in a lot of unnecessary chatter. So what’s the solution? Cache the parameter collection, of course!

Here’s a very simple way to do just that. I have an abstract class that maintains a static dictionary of SqlParameterCollections, indexed by SQL command. When the static DeriveParameters method is called from within derived classes, the dictionary is checked. If a matching parameter collection is found, it will be used to create a new parameter collection using the existing collection as a blueprint. If no collection is found, SqlCommandBuilder.DeriveParameters is used, and the resulting collection is cached.

public abstract class BaseDataProvider
{
    private static readonly Dictionary<string, SqlParameterCollection> _sqlParameterCollections;

    static BaseDataProvider()
    {
        _sqlParameterCollections = new Dictionary<string, SqlParameterCollection>();
    }

    private static void DeriveParameters(SqlCommand cmd)
    {
        if (_sqlParameterCollections.ContainsKey(cmd.CommandText))
        {
            var paramCollection = _sqlParameterCollections[cmd.CommandText];
            cmd.Parameters.AddRange(
                paramCollection.OfType<SqlParameter>()
                    .Select(x => new SqlParameter
                    {
                        ParameterName = x.ParameterName,
                        SqlDbType = x.SqlDbType,
                        Size = x.Size,
                        Direction = x.Direction
                    })
                    .ToArray()
                );
            return;
        }

        SqlCommandBuilder.DeriveParameters(cmd);
        _sqlParameterCollections.Add(cmd.CommandText, cmd.Parameters);
    }
}

Note that caching stored procedure parameters can have a negative side-effect: if the parameters DO change, your cache may need to be refreshed. This could be handled in a number of ways. You could have the cached parameters expire after a certain amount of time, you could have a manual trigger, or you could build logic into your application that automatically refreshes the cache if the appropriate error condition is detected.

Update 10/10/2012:

The SqlParameter class also implements ICloneable. I’ve updated my caching logic to take advantage of this. Note that you must cast the parameter to ICloneable in order to invoke its Clone method.

private static readonly Dictionary<string, SqlParameter[]> ParameterCache;

// retrieve from cache
if (ParameterCache.ContainsKey(cmd.CommandText))
{
	var paramCollection = ParameterCache[cmd.CommandText];
	cmd.Parameters.AddRange(
		paramCollection.OfType<SqlParameter>()
			.Select(x => ((ICloneable)x).Clone() as SqlParameter)
			.ToArray()
		);
	return;
}

// add to cache
var parameters = cmd.Parameters
	.OfType<SqlParameter>()
	.Select(x => ((ICloneable)x).Clone() as SqlParameter)
	.ToArray();
ParameterCache.Add(cmd.CommandText, parameters);
Advertisements

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s