2011-12-06 4 views
6

Mam niewielki problem z wyborem najlepszego sposobu na zreorganizowanie metody zawierającej zapytania LINQ, które są bardzo podobne, ale nie identyczne.Metoda refaktoryzacji zawierająca zapytania LINQ

Rozważmy metodę, która jest coś wzdłuż tych linii:

public SomeObject GetTheObject(IMyObject genericObject) { 
    Type t = genericObject.GetType(); 
    SomeObject so = null; 

    switch(t.Name) { 
     case "Type1": 
      var object1 = (from o in object1s where o.object1id == genericObject.id).FirstOrDefault(); 
      so = (SomeObject)object1; 
     break; 
     case "Type2": 
      var object2 = (from o in object2s where o.object2id == genericObject.id).FirstOrDefault(); 
      so = (SomeObject)object2; 
     break; 
     default: 
     break; 
    } 

    return so; 
} 

To tylko ilustracja, ale wyobraź sobie, ja potrzebuje na wykonanie innego zapytania (różni się tym, że używa innego ObjectSet, wykorzystuje niewielkie różne pola (object1id vs object2id) i zwraca inny typ Poza tym kwerendy są takie same:

Czy istnieje rozsądny sposób na refaktoryzację tego rodzaju metody? Wydaje się, że brakowało mi czegoś oczywistego. Być może muszę użyć dokładnej metody i nie mogę uniknąć ponownego napisania zapytania, to tylko se ems jak powinienem być w stanie jakoś!

Wszelkie wskazówki mile widziana

+0

próbowałem go z refleksji, ale nie mógł ominąć rachunku LINQ ("od o w obiektach, gdzie o.object1id"). Powinieneś przyjrzeć się dynamicznemu generowaniu instrukcji LINQ. – Graham

+0

Witam Graham, z pewnością byłaby to opcja z wyjątkiem faktu, że staram się przechowywać DAL zamknięty w repozytorium , co pozostawia mnie ograniczoną w metodach, których mogę użyć do dynamicznego budowania zapytania. Próbowałem zbudować fabrykę, aby zwrócić konkretną instancję Repozytorium, której potrzebowałem. Ale to pozostawiło mnie w sytuacji podobnej do opisanej przez Paolo, ponieważ moje repozytorium wymaga konkretnego typu EntityObject, którego nie mogę utworzyć opartego na interfejsie. – dougajmcdonald

Odpowiedz

4

Może właśnie uproszczony swój scenariusz, ale śmierdzący część swojej funkcji jest oddanych do SomeObject. Nie możesz po prostu pracować z interfejsami i (jeśli to konieczne) rzutować wynik na stronie połączenia? Możesz mieć swój typ Type1 i Type2 we wspólnym interfejsie, gdzie id1 i id2 są naświetlone jako id, na przykład (lub dekoruj je, jeśli nie kontrolujesz Type1 i Type2).

I.e.

public static IMyObject GetTheObject(List<IMyObject> theList, int id) 
{ 
    var ret = (from o in theList 
     where o.id==id 
     select o).FirstOrDefault(); 

    return ret; 
} 

Na przykład, jeśli masz:

public interface IMyObject {int id {get;}} 

    public class Foo : IMyObject {public int id {get; set;}} 
    public class Bar : IMyObject {public int id {get; set;}} 

można zrobić:

var l1 = new List<IMyObject>(){new Foo(){id=1}, new Foo(){id=2}}; 
var l2 = new List<IMyObject>(){new Bar(){id=1}, new Bar(){id=2}}; 

var obj1 = Test.GetTheObject(l1, 1); 
var obj2 = Test.GetTheObject(l2, 2); 

i oddanych obiektów po wywołaniu funkcji, jeśli trzeba.

EDIT: jeśli utkniesz z konkretnych przedmiotów i odlewów, najlepiej refaktoring mogłem wymyślić jest:

public static SomeObject GetTheObject(IMyObject genericObject) { 
    Type t = genericObject.GetType(); 

    Func<SomeObject, bool> WhereClause = null; 
    IEnumerable<SomeObject> objs = null; // IEnumerable<T> is covariant, 
         // so we can assign it both an IEnumerable<object1> 
         // and an IEnumerable<object2> (provided object1 and 2 are 
         // subclasses of SomeObject) 

    switch(t.Name) { 
     case "Type1": 
      WhereClause = o => ((Object1)o).object1id == genericObject.id;  
      objs = object1s; 
     break; 
     case "Type2": 
      WhereClause = o => ((Object2)o).object2id == genericObject.id;  
      objs = object2s; 
     break; 
    } 

    var ob = objs 
    .Where(WhereClause) 
    .FirstOrDefault(); 

    return (SomeObject)ob; 
} 
+0

Tak, wydaje mi się, że trochę upraszczałem, problem z obsługą interfejsów polega na tym, że moje zapytanie to właściwie zapytanie do ogólnego repozytorium, które wymaga konkretnego typu. To oznacza, że ​​nie mogę zrobić Respository w tej chwili, która wywołuje we mnie smutek. Mogę przypuszczać, że napisać kolejne repozytorium, w którym T: IMyObject, a nie T: EntityObject jest w tej chwili, ale to wydaje się przesadą, ale może być potrzebne. – dougajmcdonald

+0

@dougajmcdonald: refaktoryzacja, którą mogłem wymyślić, nie wygląda na ulepszenie twojego, ale przynajmniej zmniejsza duplikację w kodzie linq ... :) –

+0

dzięki za to, będę miał grę, kiedy dostanę szansa w czwartek, miałem nadzieję odejść od logiki przełączania, ponieważ w mojej prawdziwej sytuacji życiowej mogę mieć 10-12 opcji i wolałbym, żeby na ekranie nie wyświetlały się instrukcje dotyczące spraw!to znowu może być jedyna opcja! – dougajmcdonald