I have a generic dictionary that pass to a method which only accepts IQueryable
as a parameter
Is it possible to cast the queryable back 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 does not make much sense. But in the big picture I have an interface which requires me to return an IQueryable
as a dataSource. On implementation returns a dictionary. On a different place in my code I have classes that process the dataSources.
The processor knows that the dataSource will be an Dictionary but I don't want to have the overhead for creating another Dictionary if I already have one.
The .AsQueryable()
extension method returns an instance of the EnumerableQuery<T>
wrapper class if it is called on something that was not already an IQueryable<T>
.
This wrapper class has an .Enumerable
property with internal
access that provides access to the original object that .AsQueryable()
was called on. So you could do this to get back your 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 generally a pretty bad idea. internal
members have their access restricted for a reason, and I don't think there's any guarantee that the internal implementation of .AsQueryable()
won't change at some point in the future. So your best bet is to either find a way to make the original dictionary accessible, or go ahead and make a new one.
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 would both act as a wrapper for the dictionary's IQueryable<T>
and provide access to the original dictionary. But on the other hand, anyone trying to retrieve the dictionary would have to know what the generic type parameters were (e.g. <string, string>
, <int, string>
, etc.) in order to cast it successfully.
The main problem here is that IQueryable wraps itself around the Dictionary rather than being like IEnumerable<> over IDictionary<>, where you could cast it back.
You can certainly find out if the type wrapped is a dictionary if you know the types involved:
public bool isDictionary<T>(object obj) {
return obj.GetType().GenericTypeArguments.Contains(typeof(T));
}
isDictionary<KeyValuePair<string,string>>(dataSource);
If you don't mind reaching into the objects internals, you could use the private Enumerable
field on EnumerableQuery
to get a version of (possibly) the original dictionary back as an IEnumerable<>
But to actually convert from an EnumerableQuery<KeyValuePair<int,int>>
hiding under an IQueryable
without doing that I think you'd have to just take the hit and create a new dictionary from it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With