Why does LINQ-to-SQL sometimes allow me to design with a function, but sometimes it is not?

It really puzzles me. I know that LINQ-to-SQL processes the selection by processing the expression tree and trying to translate the material through the generated query, so some function translations do not work ( string.IsNullOrWhitespace is a regular annoyance).

I got into a situation in my code where LINQ-to-SQL was able to call my helper function via Select Select ... sometimes. In fact, it works when the method has one name, but does not work with it, it has a different name. I think this is best illustrated by the following program (it is about as simple as I could do this):

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

The database database has only one table named [Number] with one column [Value] INT NOT NULL .

As written, the program works for me, but uncommenting #define at the top will switch the name of the method call, and then the program will NotSupportedException when executing ToList() .

I tried several different method names to try to define a pattern, but could not. It looks like if a method is named that starts with To , it will throw a NotSupportedException , but it seems to work with any other name. This is still weird.

Can someone explain what is happening?

Here's another example without the #define switch, it just executes both methods back, and I still see the same problem. In particular, DoIt works, but ToValueType does not work.

 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, } } } 
+5
source share
1 answer

This is an L2S error. This is expected to work.

In the decompiled source code, I find:

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

Search "To" :

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

Hmm ... There is another place. Perhaps L2S thinks your value types are decimal. Try adding the second argument to break the mc.Arguments.Count == 1 condition.

In any case, this is a mistake. There is no deeper reason for this. Typically, L2S can perform functions in the finale of Select just fine. This is an awesome feature that EF is still missing.

Put this to rest and move to EF as soon as possible. L2S is abandoned.

+4
source

Source: https://habr.com/ru/post/1239835/


All Articles