Turning an IQueryable into a simple UnitOfWork pattern

Sometimes it is necessary to associate the lifespan of a data context with an IQueryable. The main reason for this is that you used data shaping to build up a context suitable for your needs – either through using DataLoadOptions in Linq to SQL or the Include method in Entity Framework. If you don’t want to eager load your whole data model, but, want to re-query your database along the defined associations you need a context that stays alive for the lifespan of your query. In case of Linq to SQL you might also need to re-shape your model which does not work with an existing DataContext. Normally, a unit of work implementation would deal with such things, however, often a complete unit of work implementation is just too heavy. For these cases this article proposes a solution, that turns an IQueryable into a simple implementation of the unit of work pattern.

Recently I was doing some report development. In order to make the report generation easy I built up a class hierarchy for the report system. One of the base classes created a Linq to SQL DataContext and I was planning in to keep it until the generation of a report is finished. Making things even more convenient I used a DataLoadOptions to shape the data necessary for the report and defining which objects should be loaded together.

First, things went fairly smooth, but then I needed additional data that htd to be shaped differently. In order to achieve this I created a new DataLoadOption and set it to the existing context. Then executed the second query, and … got an exception. Apparently, the DataContext did not like it when you set the LoadOptions again

While looking for a solution I came accross Kevin Wattkins’ post of how to change the LoadOptions on an active DataContext. However, there can be some problems with this approach as you might get different results when you re-execute a query and the LoadOptions has changed. So – for my project – I did not want to go down this road.

Normally, the described scenario can be solved by using the UnitOfWork and Repository patterns. But re-designing the application to use this pattern did not really make sense and would have meant too much work.

After further research, I got the idea of turning an IQueryable into a simple version of the UnitOfWork pattern. For this I would need to associate the the context with the IQueryable and make it disposable. I then looked around how I can extend an IQueryable and found Alex D. James’ post, which describes exactly this.

Once I found the article, it was only a matter of a couple of hours till I came up with my IContextualQueryable<T>, which associates a context with an IQueryable<T> and disposes it, once the query gets out of scope or Dispose() is called.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Collections;
using System.Reflection;

namespace UnitOfWorkQueryable
{
	public interface IContextualQueryable<TElement> : IOrderedQueryable<TElement>, IDisposable { }

	public static class ContextualQueryable
	{
		public static IContextualQueryable<TElement> Create<TContext, TElement>(TContext context, IQueryable<TElement> underlyingQuery)
			where TContext : IDisposable 
		{
			return ContextualQueryProviderImpl<TContext>.Create<TElement>(context, underlyingQuery);
		}

		public static IContextualQueryable<TElement> AsContextualQueryable<TElement, TContext>(this IQueryable<TElement> query, TContext context)
			where TContext : IDisposable
		{
			return Create(context, query);
		}

		private class ContextualQueryableImpl<TContext, TElement> : IContextualQueryable<TElement>, IOrderedQueryable<TElement>, IDisposable
			where TContext : IDisposable
		{
			internal ContextualQueryableImpl(ContextualQueryProviderImpl<TContext> provider, TContext context, Expression expression) {
				_provider = provider;
				_context = context;
				_expression = expression;
			}

			public void Dispose() {
				if (_context != null)
					_context.Dispose();
			}

			private TContext _context;
			private Expression _expression;
			private ContextualQueryProviderImpl<TContext> _provider;

			public IEnumerator<TElement> GetEnumerator() {
				return this._provider.ExecuteQuery<TElement>(this._expression);
			}

			IEnumerator IEnumerable.GetEnumerator() {
				return this._provider.ExecuteQuery<TElement>(this._expression);
			}

			public Type ElementType {
				get { return typeof(TElement); }
			}

			public Expression Expression {
				get { return this._expression; }
			}

			public IQueryProvider Provider {
				get { return this._provider; }
			}
		}

		private class ContextualQueryProviderImpl<TContext> : IQueryProvider
			where TContext : IDisposable
		{
			internal static IContextualQueryable<TElement> Create<TElement>(TContext context, IQueryable<TElement> underlyingQuery) {
				var provider = new ContextualQueryProviderImpl<TContext>(context, underlyingQuery.Provider);
				return (IContextualQueryable<TElement>) provider.CreateQuery<TElement>(underlyingQuery.Expression);
			}

			private ContextualQueryProviderImpl(TContext context, IQueryProvider underlyingProvider) {
				this._underlyingProvider = underlyingProvider;
				this._context = context;
			}

			private IQueryProvider _underlyingProvider;
			private TContext _context;

			public IQueryable<TElement> CreateQuery<TElement>(Expression expression) {
				return new ContextualQueryableImpl<TContext, TElement>(this, this._context, expression);
			}

			public IQueryable CreateQuery(Expression expression) {
				Type et = FindIEnumerable(expression.Type);
				Type qt = typeof(ContextualQueryableImpl<,>).MakeGenericType(typeof(TContext), et);
				object[] args = new object[] { this, _context, expression };

				ConstructorInfo ci = qt.GetConstructor(
					BindingFlags.NonPublic | BindingFlags.Instance,
					null,
					new Type[] { 
                typeof(ContextualQueryProviderImpl<>).MakeGenericType(typeof(TContext)),
				typeof(TContext),
                typeof(Expression)
            },
					null);

				return (IQueryable) ci.Invoke(args);
			}

			public TResult Execute<TResult>(Expression expression) {
				return this._underlyingProvider.Execute<TResult>(expression);
			}
			public object Execute(Expression expression) {
				return this._underlyingProvider.Execute(expression);
			}

			public IEnumerator<TElement> ExecuteQuery<TElement>(Expression expression) {

				return _underlyingProvider.CreateQuery<TElement>(expression).GetEnumerator();
			}

			private Type FindIEnumerable(Type seqType) {
				if (seqType == null || seqType == typeof(string))
					return null;
				if (seqType.IsArray)
					return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType());
				if (seqType.IsGenericType) {
					foreach (Type arg in seqType.GetGenericArguments()) {
						Type ienum = typeof(IEnumerable<>).MakeGenericType(arg);
						if (ienum.IsAssignableFrom(seqType)) {
							return ienum;
						}
					}
				}
				Type[] ifaces = seqType.GetInterfaces();
				if (ifaces != null && ifaces.Length > 0) {
					foreach (Type iface in ifaces) {
						Type ienum = FindIEnumerable(iface);
						if (ienum != null) return ienum;
					}
				}
				if (seqType.BaseType != null && seqType.BaseType != typeof(object)) {
					return FindIEnumerable(seqType.BaseType);
				}
				return null;
			}
		}
	}
}

The code implements the necessary IQueryable and IQueryProvider interfaces, and adds a method for creating an IContextualQuery<T> or turning a normal IQueryable<T> into an IContextualQuery<T>. The implementation is fairly straight forward.

The only thing I like to mention, though, is that I decided against making the context available to the user of the interface. It would have meant explicitly typing the DataContext instead only requiring that it implements IDisposable. This would also have meant that the ContextualQueryable would have been bound to Linq to SQL or to the Entity Framework. By hiding the context this limitation is not present and the implementation can be used with both data access methods.

Furthermore, by hiding the context, the user is will be prevented from using the context for additional new queries or even updates. For both cases, I frimly believe that you should the context associated with the query, but to create a new context within you scope.

The following code shows how the ContextualQueryable is used:

try {
	// Linq to SQL
	var ctx = new DataClasses1DataContext();
	var query = ContextualQueryable.Create(ctx, ctx.Customers);
	ctx = null;
	var ordersFirst = query.First().Orders;
	query.Dispose();

	// throws an exception as context is disposed
	var ordersLast = query.Last().Orders;

	ctx = new DataClasses1DataContext();
	query = ctx.Customers.AsContextualQueryable(ctx);
} catch { }

try {
	// Entity Framework
	var ctx = new EntityFramework.NorthwindEntities();
	var query = ContextualQueryable.Create(ctx, ctx.Customers);
	ctx = null;
	var ordersFirst = query.First().Orders;
	query.Dispose();

	// throws an exception as context is disposed
	var ordersLast = query.Last().Orders;

	ctx = new EntityFramework.NorthwindEntities();
	query = ctx.Customers.AsContextualQueryable(ctx);
} catch { }

About Axel Eckenberger

Working as a Senior Software Devloper in Munich (Germany).
This entry was posted in Programming and tagged , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*


4 − = one

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>