2016-01-04 23 views
5

To naprawdę mnie zastanawia. Wiem, że LINQ-SQL obsługuje selekcje, przetwarzając drzewo wyrażeń i próbując przetłumaczyć je za pomocą wygenerowanego zapytania, dlatego niektóre tłumaczenia funkcji nie działają poprawnie (string.IsNullOrWhitespace to zwykła irytacja).Dlaczego LINQ-SQL czasami pozwala mi na projekt przy użyciu funkcji, ale czasami nie?

Uderzyłem w sytuację w moim kodzie, w którym LINQ-SQL mógł wywoływać moją funkcję pomocnika za pośrednictwem Wybierz projekcję ... czasami. W rzeczywistości działa, gdy metoda ma jedną nazwę, ale nie działa z nią ma inną nazwę. Myślę, że jest to najlepiej ilustruje poniższy program (to jest tak proste, jak mogłoby uczynić go):

// #define BREAK_THE_CODE 

using System; 
using Sandbox.Data; 
using System.Collections.Generic; 
using System.Linq; 

namespace Sandbox.Console 
{ 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      using (var dataContext = new SandboxDataContext()) 
      { 
       List<MyValue> myValueList; 

       try 
       { 
        myValueList = dataContext.Numbers.Select(x => new MyValue 
        { 
#if BREAK_THE_CODE 
         Type = ToValueType(x.Value), 
#else 
         Type = DoIt(x.Value), 
#endif 
        }).ToList(); 
       } 
       catch (NotSupportedException) 
       { 
        System.Console.WriteLine("Not supported, oh noes!"); 
        System.Console.ReadKey(); 
        return; 
       } 

       System.Console.WriteLine(myValueList.Count); 
       System.Console.ReadKey(); 
      } 
     } 

#if BREAK_THE_CODE 
     public static MyValueType ToValueType(int value) 
#else 
     public static MyValueType DoIt(int value) 
#endif 
     { 
      return MyValueType.One; 
     } 

     public sealed class MyValue 
     { 
      public MyValueType Type { get; set; } 
     } 

     public enum MyValueType 
     { 
      One, 
      Two, 
      Unknown, 
     } 
    } 
} 

Baza podkładowa ma tylko jedną tabelę o nazwie [Number] z pojedynczej kolumny [Value] INT NOT NULL.

Jak napisano, program działa na mnie, ale odkomentowanie #define na szczycie przełączy nazwę wywołania metody, a następnie program będzie rzucać NotSupportedException podczas wykonywania ToList().

Próbowałem różnych nazw metod, aby spróbować określić wzór, ale nie udało się. Wygląda na to, że jeśli metoda otrzyma nazwę o numerze zaczynającym się od To, to wyśle ​​ona NotSupportedException, ale wydaje się działać z dowolną inną nazwą. To wciąż jest dziwne.

Czy ktoś może wyjaśnić, co się dzieje?

Oto kolejna próbka bez przełącznika #define, to po prostu robi obie metody z powrotem i nadal widzę ten sam problem. W szczególności działa DoIt, ale ToValueType nie.

using System; 
using Sandbox.Data; 
using System.Linq; 

namespace Sandbox.Console 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      using (var dataContext = new SandboxDataContext()) 
      { 
       try 
       { 
        var myValueList = dataContext.Numbers.Select(x => new MyValue 
        { 
         Type = DoIt(x.Value), 
        }).ToList(); 

        System.Console.WriteLine("DoIt Succeeded, found {0} results", myValueList.Count); 
       } 
       catch (NotSupportedException) 
       { 
        System.Console.WriteLine("DoIt Failed, oh noes!"); 
       } 

       try 
       { 
        var myValueList = dataContext.Numbers.Select(x => new MyValue 
        { 
         Type = ToValueType(x.Value), 
        }).ToList(); 

        System.Console.WriteLine("ToValueType Succeeded, found {0} results", myValueList.Count); 
       } 
       catch (NotSupportedException) 
       { 
        System.Console.WriteLine("ToValueType Failed, oh noes!"); 
       } 

       System.Console.ReadKey(); 
      } 
     } 

     public static MyValueType DoIt(int value) 
     { 
      return MyValueType.SpecialType; 
     } 

     public static MyValueType ToValueType(int value) 
     { 
      return MyValueType.SpecialType; 
     } 

     public sealed class MyValue 
     { 
      public MyValueType Type { get; set; } 
     } 

     public enum MyValueType 
     { 
      SpecialType, 
     } 
    } 
} 
+0

Czy ToValueType jedyny ciąg, który nie działa? Jeśli to prawda, co mówisz, i jestem ostrożny, zakładając, że to byłby błąd L2S (naprawdę nietypowy). – usr

+0

Zwykle L2S może wykonywać funkcje w finale Wybierz dokładnie. Jest to niesamowita funkcja, której brakuje w EF. – usr

+0

Byłem w stanie odtworzyć opisane zachowanie nawet w LINQPad, za każdym razem, gdy używasz metody zaczynającej się od 'To' (rozróżniana jest wielkość liter), zgłaszany jest wyjątek. –

Odpowiedz

4

To jest błąd L2S. Oczekuje się, że to zadziała.

W kodzie źródłowym decompiled znajdę:

private static MethodSupport GetDecimalMethodSupport(SqlMethodCall mc) 
    { 
     if (mc.Method.IsStatic) 
     { 
      if (mc.Arguments.Count == 2) 
      { 
       string str; 
       if (((str = mc.Method.Name) != null) && ((((str == "Multiply") || (str == "Divide")) || ((str == "Subtract") || (str == "Add"))) || ((str == "Remainder") || (str == "Round")))) 
       { 
        return MethodSupport.Method; 
       } 
      } 
      else if (mc.Arguments.Count == 1) 
      { 
       string str2; 
       if (((str2 = mc.Method.Name) != null) && (((str2 == "Negate") || (str2 == "Floor")) || ((str2 == "Truncate") || (str2 == "Round")))) 
       { 
        return MethodSupport.Method; 
       } 
       if (mc.Method.Name.StartsWith("To", StringComparison.Ordinal)) 
       { 
        return MethodSupport.Method; 
       } 
      } 
     } 
     return MethodSupport.None; 
    } 

wyszukiwania "To":

if (mc.Method.Name.StartsWith("To", StringComparison.Ordinal)) 

Hm ... Jest jedno inne miejsce. Może L2S myśli, że twoje typy wartości są dziesiętne. Spróbuj dodać drugi argument, aby złamać warunek mc.Arguments.Count == 1.

W każdym razie jest to błąd. Nie ma głębszego powodu za tym. Zwykle L2S może wykonywać funkcje w finale Wybierz dokładnie. Jest to niesamowita funkcja, której brakuje w EF.

Połóż to na odpoczynek i przenieś do EF jak najszybciej. L2S jest porzucony.

+0

Dzięki za informację - miałem wrażenie, że miało to związek z interpretacją nazwy metody w drzewie wyrażeń.Zmiana liczby argumentów lub zmiana na metodę instancji wydaje się rozwiązywać program (chociaż planujesz zmienić nazwę metody i pozostawiając dobry komentarz). – Anthony

+0

Również nie wygląda na to, że mój typ jest podwójny, przebiega przez kilka funkcji 'GetXMethodSupport', ale' GetDecimalMethodSupport' powinien sprawdzać zarówno 'IsStatic' i' mc.Method.DeclaringType == typeof (dziesiętnie) '- co robią inne metody pomocnicze. – Anthony

+0

Tak, to wyjaśnia. Uważa, że ​​jest to "metoda wsparcia". Rozważ głosowanie na głos użytkownika, który prosi o kod źródłowy L2S, aby takie błędy można było łatwo naprawić: https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/11277618-open-source -linq-sql – usr

Powiązane problemy