LINQ Tips #6 Create a LINQ to Anything(2)

Yesterday, we sort of created a baby step of a LINQ to anything implementation;

We stopped at the place to implement the interface IQueryProvider, the most important method in this interface for us is IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression).

Our code will look like:

   1:   #region IQueryProvider Members
   2:   
   3:          public IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)
   4:          {
   5:              IQueryable<TElement> element = null;
   6:              switch (expression.NodeType)
   7:              {
   8:                  case System.Linq.Expressions.ExpressionType.Call:
   9:                      MethodCallExpression callExpression = (MethodCallExpression)expression;
  10:                      switch (callExpression.Method.Name)
  11:                      {
  12:                          case "Select":
  13:                              element = new StudentQueryObject<TElement>(callExpression);
  14:                              break;
  15:                          default:
  16:                              throw new NotImplementedException("This method is not supported");
  17:                      }
  18:                      break;
  19:              }
  20:              return element;
  21:          }
  22:   
  23:          public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
  24:          {
  25:              return CreateQuery<Student>(expression);
  26:          }
  27:   
  28:          public TResult Execute<TResult>(System.Linq.Expressions.Expression expression)
  29:          {
  30:              MethodCallExpression callExpression = (MethodCallExpression)expression;
  31:              throw new NotImplementedException("No execution is defined");
  32:          }
  33:   
  34:          public object Execute(System.Linq.Expressions.Expression expression)
  35:          {
  36:              return Execute<Student>(expression);
  37:          }
  38:   
  39:          #endregion
 
Maybe you already noticed that in the "Select" method , We created a new class called StudentQueryObject. Before we went into this class, we can say that all the implementation we have now are kind of generic implementation and can be used under almost all situation.
 
The implementation for the StudentQueryObject class is
   1:      public class StudentQueryObject<T> : IQueryable<T>
   2:      {
   3:          private Expression _expression;
   4:   
   5:          public StudentQueryObject(Expression expression)
   6:          {
   7:              _expression = expression;
   8:          }
   9:          #region IEnumerable<T> Members
  10:   
  11:          public IEnumerator<T> GetEnumerator()
  12:          {
  13:              MethodCallExpression callExpression = (MethodCallExpression)_expression;
  14:              ClassRoom2 classroom = (ClassRoom2)(((ConstantExpression)callExpression.Arguments[0]).Value);
  15:   
  16:              UnaryExpression quote = (UnaryExpression)callExpression.Arguments[1];
  17:   
  18:              Expression<Func<Student, T>> projector = (Expression<Func<Student, T>>)quote.Operand;
  19:              Func<Student, T> func= projector.Compile();
  20:   
  21:              List<T> list = new List<T>();
  22:   
  23:              
  24:              foreach (Student student in classroom)
  25:              {
  26:                  list.Add(func(new Student { FirstName=student.FirstName +"Linq", LastName=student.LastName + "Linq" }));
  27:              }
  28:              return list.GetEnumerator();
  29:          }
  30:   
  31:          #endregion
  32:   
  33:          #region IEnumerable Members
  34:   
  35:          System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  36:          {
  37:              return GetEnumerator();
  38:          }
  39:   
  40:          #endregion
  41:   
  42:          #region IQueryable Members
  43:   
  44:          public Type ElementType
  45:          {
  46:              get { return typeof(T); }
  47:          }
  48:   
  49:          public System.Linq.Expressions.Expression Expression
  50:          {
  51:              get { return _expression; }
  52:          }
  53:   
  54:          public IQueryProvider Provider
  55:          {
  56:              get { return null; }
  57:          }
  58:   
  59:          #endregion
  60:      }
The method of the GetEnumerator can be the data provider for the LINQ query source. So basically , we 
can put the data provider logic here .In this example, I just changed the original provided data.
 
All the data will be added a word "Linq" at the tail.
 
This is a fairly simple implementation, But this can be extended to very complex scenario.The NFlickr  will have 
a full set of implementation and I will have a post after the first release of the NFlickr.
Published Monday, November 10, 2008 10:35 PM by RobertNet
Filed under: ,

Comments

No Comments

Leave a Comment

(required) 
(required) 
(optional)
(required)