Get source dictionary from dict.AsQueryable ()

I have a generic dictionary that jumps to a method that accepts IQueryable only as a parameter

Is it possible to return a query to the original dictionary? And I don't mean creating a new dictionary with .ToDictionary(...)

 private static void Main() { var dict = new Dictionary<int, int>(); dict.Add(1,1); SomeMethod(dict.AsQueryable()); } public static void SomeMethod(IQueryable dataSource) { // dataSource as Dictionary<int, int> --> null var dict = dataSource.??? } 

I know in this simple example this doesn't make much sense. But in the big picture, I have an interface that requires me to return IQueryable as a data source. When implemented, returns a dictionary. Elsewhere in my code, I have classes that handle dataSources.

The processor knows that the dataSource will be a Dictionary, but I do not want to have the overhead to create another Dictionary, if I already have one.

+7
dictionary c # iqueryable
source share
2 answers

The .AsQueryable() extension method returns an instance of the EnumerableQuery<T> class of the wrapper if it is called because IQueryable<T> has not yet been.

This wrapper class has an .Enumerable property with internal access that provides access to the source object that .AsQueryable() was called to. Therefore, you can do this to return the original dictionary:

 var dict = new Dictionary<int, int>(); dict.Add(1,1); var q = dict.AsQueryable(); Type tInfo = q.GetType(); PropertyInfo pInfo = tInfo.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance) .FirstOrDefault(p => p.Name == "Enumerable"); if (pInfo != null) { object originalDictionary = pInfo.GetValue(q, null); Console.WriteLine(dict == originalDictionary); // true } 

However, this is usually a pretty bad idea. internal members have access to them for some reason, and I don't think there is any guarantee that the internal implementation of .AsQueryable() will not change at some point in the future. Therefore, your best bet is to either find a way to make the source dictionary available, or go ahead and make a new one.


One possible workaround (which is not big) is to make your own wrapper class a dictionary wrapper:
 private class DictionaryQueryHolder<TKey, TValue> : IQueryable<KeyValuePair<TKey, TValue>> { public IDictionary<TKey, TValue> Dictionary { get; private set; } private IQueryable<KeyValuePair<TKey, TValue>> Queryable { get; set; } internal DictionaryQueryHolder(IDictionary<TKey, TValue> dictionary) { Dictionary = dictionary; Queryable = dictionary.AsQueryable(); } public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return Queryable.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public Expression Expression { get { return Queryable.Expression; } } public Type ElementType { get { return Queryable.ElementType; } } public IQueryProvider Provider { get { return Queryable.Provider; } } } 

This will act as a wrapper for the IQueryable<T> dictionary and provide access to the original dictionary. But, on the other hand, anyone trying to get a dictionary should have known that there were typical type parameters (e.g. <string, string> , <int, string> , etc.) in order to successfully execute it.

+6
source share

The main problem here is that IQueryable wraps around the Dictionary, and not as IEnumerable <> over IDictionary <>, where you can return it.

You can, of course, find out if this type is terminated if you know the types:

 public bool isDictionary<T>(object obj) { return obj.GetType().GenericTypeArguments.Contains(typeof(T)); } isDictionary<KeyValuePair<string,string>>(dataSource); 

If you don't mind getting inside objects, you can use the Enumerable private field on EnumerableQuery to get the version (possibly) of the original dictionary back as IEnumerable<>

But for the actual conversion from EnumerableQuery<KeyValuePair<int,int>> hide under IQueryable , without doing this, I think you just need to take a hit and create a new dictionary from it.

+2
source share

All Articles