2015-01-12 13 views
7

Mam rodzajowe słownika, które przechodzą do sposobu, który akceptuje tylko IQueryable jako parametrGet oryginalnego słownika z dict.AsQueryable()

to możliwe, aby rzucić queryable z powrotem do oryginalnego słownika? I nie mam na myśli tworzenia nowego słownika z .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.??? 
} 

wiem, w ten prosty przykład to nie ma sensu. Ale w ogólnym obrazie mam interfejs, który wymaga, aby zwrócić IQueryable jako źródło danych. Po wdrożeniu zwraca słownik. W innym miejscu w moim kodzie mam klasy, które przetwarzają źródła danych.

Procesor wie, że źródłem danych będzie słownik, ale nie chcę mieć narzutu na tworzenie kolejnego słownika, jeśli już go mam.

Odpowiedz

6

Metoda rozszerzenia .AsQueryable() zwraca instancję z EnumerableQuery<T> wrapper class, jeśli jest wywoływana w przypadku czegoś, co nie było jeszcze IQueryable<T>.

Ta klasa otoki ma właściwość .Enumerable z dostępem internal, która zapewnia dostęp do oryginalnego obiektu, na którym wywołano .AsQueryable(). Więc mógłby to zrobić, aby odzyskać swój pierwotny słownika:

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 
} 

Jest to jednak na ogół całkiem zły pomysł. internal członkowie mają ograniczony dostęp z jakiegoś powodu i nie sądzę, że istnieje jakakolwiek gwarancja, że ​​wewnętrzne wdrożenie .AsQueryable() nie zmieni się w którymś momencie w przyszłości. Najlepiej jest znaleźć sposób na udostępnienie oryginalnego słownika lub stworzyć nowy.


Jedną z możliwości obejścia (co nie jest wielki) jest zrobić własne klasy otoki do przeprowadzenia słownika wzdłuż:

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; } 
    } 
} 

To byłoby zarówno działać jako otoczka do słownika w IQueryable<T> i zapewniać dostęp do oryginalnego słownika . Ale z drugiej strony, każdy, kto próbuje pobrać słownik musiałby wiedzieć, jakie były parametry rodzaju ogólnego (na przykład <string, string>, <int, string>, itp.), Aby móc go pomyślnie obsłużyć.

+0

Podpowiedź z prywatnym polem 'Obliczalne' działała.Zawarłem nawet rezerwę w moim kodzie ('if (pInfo == null), następnie stwórz nowy słownik i ręcznie dodaj KeyValuePairs'), aby zachować kompatybilność z przyszłymi wersjami. –

2

Głównym problemem jest to, że IQueryable owija się wokół Słownika, a nie jest jak IZliczalny <> ponad IDictionary <>, gdzie można go odrzucić.

pewnością można dowiedzieć się, czy typ owinięty jest słownikiem jeśli znasz rodzaje uczestniczące:

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

isDictionary<KeyValuePair<string,string>>(dataSource); 

Jeśli nie przeszkadza sięgając do obiektów wewnętrznych, można użyć pola prywatne na EnumerableEnumerableQuery aby uzyskać wersji (ewentualnie) oryginalnego słownika powrotem jako IEnumerable<>

Ale faktycznie konwertować z EnumerableQuery<KeyValuePair<int,int>> ukrywanie pod IQueryable nie robiąc, że myślę, że trzeba po prostu wziąć trafienie i utworzyć nowy dictio Nary z tego.