2016-01-04 76 views
5

Questo mi sta davvero sconcertando. So che gli handle LINQ-to-SQL selezionano elaborando l'albero delle espressioni e tentando di tradurre le cose tramite la query generata, motivo per cui alcune traduzioni di funzioni non funzionano (string.IsNullOrWhitespace è un disturbo normale).Perché LINQ-to-SQL a volte mi consente di proiettare utilizzando una funzione, ma a volte no?

Ho riscontrato una situazione nel mio codice in cui LINQ-to-SQL è stato in grado di chiamare la mia funzione di supporto tramite una proiezione Seleziona ... a volte. In effetti, funziona quando il metodo ha un solo nome, ma non funziona con esso ha un altro nome. Credo che questo è meglio illustrato da seguente programma (questo è quanto di più semplice come ho potuto fare):

// #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, 
     } 
    } 
} 

Il database di supporto ha una sola tabella denominata [Number] con una sola colonna [Value] INT NOT NULL.

Come scritto, il programma funziona per me, ma commentando la #define in alto passerà il nome della chiamata di metodo e quindi il programma sarà gettare un NotSupportedException durante l'esecuzione ToList().

Ho provato diversi nomi di metodi per provare a determinare un modello, ma non sono stato in grado di farlo. Sembra che il metodo abbia un nome qualsiasi che inizi con To e che lanci lo NotSupportedException, ma sembra funzionare con qualsiasi altro nome. Questo è ancora strano.

Qualcuno può spiegare cosa sta succedendo?

Ecco un altro esempio senza l'interruttore #define, è sufficiente eseguire entrambi i metodi in modalità back to back e sto ancora vedendo lo stesso problema. Nello specifico, DoIt funziona, ma non lo è ToValueType.

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

È ToValueType l'unica stringa che non funziona? Se è vero quello che dici, e sono prudente supporre che, questo sarebbe un bug L2S ​​(davvero insolito). – usr

+0

Normalmente, L2S può eseguire le funzioni nella selezione finale. Questa è una caratteristica fantastica di EF che manca ancora. – usr

+0

Sono stato in grado di riprodurre il comportamento che hai descritto anche in LINQPad, ogni volta che usi un metodo che inizia con 'To' (sensibile al maiuscolo/minuscolo) genera un'eccezione. –

risposta

4

Questo è un bug L2S. Questo dovrebbe funzionare.

Nel codice sorgente decompilato trovo:

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

Ricerca di "To":

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

Hm ... C'è un altro posto. Forse L2S pensa che i tuoi tipi di valore siano decimali. Prova ad aggiungere un secondo argomento per rompere la condizione mc.Arguments.Count == 1.

In ogni caso, questo è un bug. Non c'è una ragione più profonda dietro questo. Normalmente, L2S è in grado di eseguire funzioni nella selezione finale. Questa è una caratteristica fantastica di EF che manca ancora.

Posare questo per riposare e migrare su EF appena possibile. L2S è abbandonato.

+0

Grazie per le informazioni - ho avuto la sensazione che avesse a che fare con l'interpretazione del nome del metodo nell'albero delle espressioni.Cambiando il conteggio degli argomenti o cambiando in un metodo di istanza sembra risolvere il programma (anche se si pianifica di rinominare il metodo e lasciare un buon commento). – Anthony

+0

Inoltre, non sembra che il mio tipo sia un double, che funzioni attraverso un po 'di funzioni 'GetXMethodSupport', ma' GetDecimalMethodSupport' dovrebbe controllare sia 'IsStatic' che' mc.Method.DeclaringType == typeof (decimale) '- che è quello che stanno facendo gli altri metodi di supporto. – Anthony

+0

Sì, questo lo spiega. Pensa che sia un "metodo di supporto". Si prega di prendere in considerazione la votazione per la voce Voce Utente che chiede di aprire l'L2S in modo che tali errori possano essere risolti facilmente: https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/11277618-open-source -linq-to-sql – usr