Click here to Skip to main content
15,886,788 members
Articles / Programming Languages / C# 4.0

Dynamically evaluated SQL LINQ queries

Rate me:
Please Sign up or sign in to vote.
4.95/5 (35 votes)
30 Nov 2013CPOL8 min read 192.2K   2.6K   116  
Extension methods to evaluate plain text SQL queries against IEnumerable collections.
using System;
using System.Dynamic;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

namespace SqlLinq.SyntaxTree.Clauses
{
    static class GroupByCallFactory
    {
        public static GroupByCall<TSource, TResult> Create<TSource, TResult>(string keyName)
        {
            if (typeof(TResult) == typeof(object))
                return new GroupByExpando<TSource, TResult>(keyName);

            if (typeof(TResult).GetMethod("Add", typeof(TResult).GetGenericArguments()) != null)
                return new GroupByIntoDictionary<TSource, TResult>(keyName);

            if (typeof(TResult).HasEmptyConstructor())
                return new GroupByMemberInit<TSource, TResult>(keyName);

            return new GroupByConstructor<TSource, TResult>(keyName);
        }
    }

    abstract class GroupByCall<TSource, TResult>
    {
        protected GroupByCall(string keyName)
        {
            Debug.Assert(!string.IsNullOrEmpty(keyName));

            Aggregates = new Dictionary<string, Delegate>(StringComparer.OrdinalIgnoreCase);
            KeyName = keyName;
        }

        public IDictionary<string, Delegate> Aggregates { get; private set; }

        public string KeyName { get; private set; }

        public abstract TResult Evaluate(object key, IEnumerable<TSource> source);
    }

    class GroupByIntoDictionary<TSource, TResult> : GroupByCall<TSource, TResult>
    {
        public GroupByIntoDictionary(string keyName)
            : base(keyName)
        {
        }

        public override TResult Evaluate(object key, IEnumerable<TSource> source)
        {
            Type dictionaryType = typeof(Dictionary<,>).MakeGenericType(typeof(TResult).GetGenericArguments());

            object dictionary = Activator.CreateInstance(dictionaryType);
            MethodInfo addMethod = dictionaryType.GetMethod("Add", typeof(TResult).GetGenericArguments());

            // KeyName is the field being grouped on - so add it
            addMethod.Invoke(dictionary, new object[] { KeyName, key });

            foreach (KeyValuePair<string, Delegate> pair in Aggregates)
                addMethod.Invoke(dictionary, new object[] { pair.Key, pair.Value.DynamicInvoke(source) });

            return (TResult)dictionary;
        }
    }

    class GroupByExpando<TSource, TResult> : GroupByCall<TSource, TResult>
    {
        public GroupByExpando(string keyName)
            : base(keyName)
        {
        }

        public override TResult Evaluate(object key, IEnumerable<TSource> source)
        {
            IDictionary<string, object> expando = (IDictionary<string, object>)new ExpandoObject();
            expando.Add(KeyName, key);

            foreach (KeyValuePair<string, Delegate> pair in Aggregates)
                expando.Add(pair.Key, pair.Value.DynamicInvoke(source));

            return (TResult)(object)expando;
        }
    }

    class GroupByConstructor<TSource, TResult> : GroupByCall<TSource, TResult>
    {
        public GroupByConstructor(string keyName)
            : base(keyName)
        {
        }

        public override TResult Evaluate(object key, IEnumerable<TSource> source)
        {
            List<object> list = new List<object>();
            list.Add(key);

            foreach (KeyValuePair<string, Delegate> pair in Aggregates)
                list.Add(pair.Value.DynamicInvoke(source));

            return (TResult)Activator.CreateInstance(typeof(TResult), list.ToArray());
        }
    }

    class GroupByMemberInit<TSource, TResult> : GroupByCall<TSource, TResult>
    {
        public GroupByMemberInit(string keyName)
            : base(keyName)
        {
        }

        public override TResult Evaluate(object key, IEnumerable<TSource> source)
        {
            TResult result = Activator.CreateInstance<TResult>();
            typeof(TResult).GetProperty(KeyName).SetValue(result, key, null);

            foreach (KeyValuePair<string, Delegate> pair in Aggregates)
            {
                object o = pair.Value.DynamicInvoke(source);
                typeof(TResult).GetProperty(pair.Key).SetValue(result, o, null);
            }

            return result;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader Starkey Laboratories
United States United States
The first computer program I ever wrote was in BASIC on a TRS-80 Model I and it looked something like:
10 PRINT "Don is cool"
20 GOTO 10

It only went downhill from there.

Hey look, I've got a blog

Comments and Discussions