2012-04-02 22 views
24

Chcę wstawić do mojej tabeli kolumnę o nazwie "S", która otrzyma pewną wartość ciągu na podstawie wartości uzyskanej z kolumny tabeli.Wywołanie metody wewnątrz kwerendy Linq

Na przykład: for each ID (a.z) Chcę pobrać jego wartość ciągu zapisaną w innej tabeli. Wartość łańcucha jest zwracana z innej metody, która pobiera ją przez zapytanie Linq.

  • Czy można wywołać metodę z Linq?
  • Czy powinienem zrobić wszystko w tym samym zapytaniu?

Jest to struktura informacji muszę dostać:

a.z is the ID in the first square in table #1, from this ID I get another id in table #2, and from that I can get my string value that I need to display under column 'S'.
enter image description here

var q = (from a in v.A join b in v.B 
    on a.i equals b.j 
    where a.k == "aaa" && a.h == 0 
    select new {T = a.i, S = someMethod(a.z).ToString()}) 
    return q; 

Linię S = someMethod(a.z).ToString() powoduje następujący błąd:

Unable to cast object of type 'System.Data.Linq.SqlClient.SqlColumn' to type 'System.Data.Linq.SqlClient.SqlMethodCall'.

+0

Twoje pytanie jest dość niejasne (dla mnie w każdym razie) - jakie tabele to dotyczy ? Czy to Linq do Entities lub Linq to Objects? Pomocne mogą być również bardziej znaczące właściwości i nazwy tabel. – BrokenGlass

+0

Przepraszam, jestem nowy w Linq, co masz na myśli Linq do Entities lub Linq to Objects? – user990635

+0

Twój komunikat o błędzie oznacza, że ​​korzystasz z Linq do SQL - tak właśnie uzyskujesz dostęp do bazy danych – BrokenGlass

Odpowiedz

41

Trzeba wykonać swoje wywołanie metody w kontekście Linq-to-Objects, ponieważ po stronie bazy danych, która nie będzie wywołanie metody sensu - można to zrobić za pomocą AsEnumerable() - w zasadzie reszta kwerendy zostaną następnie ocenione jako w kolekcji pamięci za pomocą Linq-to-Objects i można korzystać wywołuje metodę zgodnie z oczekiwaniami:

var q = (from a in v.A join b in v.B 
     on a.i equals b.j 
     where a.k == "aaa" && a.h == 0 
     select new {T = a.i, Z = a.z }) 
     .AsEnumerable() 
     .Select(x => new { T = x.T, S = someMethod(x.Z).ToString() }) 
+0

Świetna odpowiedź, dziękuję – EgyEast

+0

Po prostu chcę się upewnić, więc iteruje ona Enumerable dwa razy, prawda? Po pierwsze, przed '.AsEnumerable()' i po drugie, po '.AsEnumerable()'? –

8

będziemy chcieli ją podzielić do dwóch zdań. Zwróć wyniki z zapytania (co spowoduje trafienie w bazę danych), a następnie wyliczy wyniki po raz drugi w oddzielnym kroku, aby przekształcić tłumaczenie na listę nowych obiektów. To drugie "zapytanie" nie trafi do bazy danych, więc będziesz mógł użyć wewnątrz niego someMethod().

Linq-to-Entities to trochę dziwna rzecz, ponieważ sprawia, że ​​przejście do zapytania bazy danych z C# jest wyjątkowo proste: ale zawsze musisz sobie przypominać: "To C# zostanie przetłumaczone na jakiś SQL . " W rezultacie musisz zadać sobie pytanie: "Czy całe to C# może zostać wykonane jako SQL?" Jeśli nie może - jeśli dzwonisz pod numer someMethod() - twoje zapytanie będzie miało problemy. A zwykłym rozwiązaniem jest rozdzielenie go.

(Inna odpowiedź od @BrokenGlass korzystając .AsEnumerable(), jest po prostu kolejny sposób, aby to osiągnąć.)

0

to stare pytanie, ale Widzę, że nikt nie wspomniał o jednym "hack", który pozwala cal l metody podczas wyboru bez powtarzania. Pomysł polega na użyciu konstruktora i konstruktora, który możesz wywoływać, cokolwiek chcesz (przynajmniej działa dobrze w LINQ z NHibernate, nie jesteś pewien LINQ2SQL lub EF, ale myślę, że powinien być taki sam). Poniżej mam kod źródłowy dla programu testowego, wygląda na to, że podejście powtarzające w moim przypadku jest o dwa razy wolniejsze niż podejście konstruktorskie i myślę, że nie ma się co dziwić - moja logika biznesowa była minimalna, więc takie rzeczy jak iteracja i przydzielanie pamięci mają znaczenie.

Również chciałem nie było lepszego sposobu, aby powiedzieć, że to czy tamto nie powinien być sądzony, aby wykonać na bazie

// Here are the results of selecting sum of 1 million ints on my machine: 
// Name Iterations  Percent  
// reiterate  294  53.3575317604356%  
// constructor  551  100% 

public class A 
{ 
    public A() 
    {    
    } 

    public A(int b, int c) 
    { 
     Result = Sum(b, c); 
    } 

    public int Result { get; set; } 

    public static int Sum(int source1, int source2) 
    { 
     return source1 + source2; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var range = Enumerable.Range(1, 1000000).ToList(); 

     BenchmarkIt.Benchmark.This("reiterate",() => 
      { 
       var tst = range 
        .Select(x => new { b = x, c = x }) 
        .AsEnumerable() 
        .Select(x => new A 
        { 
         Result = A.Sum(x.b, x.c) 
        }) 
        .ToList(); 
      }) 
      .Against.This("constructor",() => 
      { 
       var tst = range 
        .Select(x => new A(x, x)) 
        .ToList(); 
      }) 
      .For(60) 
      .Seconds() 
      .PrintComparison(); 

     Console.ReadKey(); 
    } 
}