This is really puzzling me. I know LINQ-to-SQL handles selects by processing the expression tree and attempting to translate stuff via the generated query, which is why some function translations don't work property (string.IsNullOrWhitespace is a regular annoyance).
I hit a situation in my code where LINQ-to-SQL was able to call my helper function via a Select projection ... sometimes. In fact, it works when the method has one name, but doesn't work with it has another name. I think this is best illustrated by the following program (this is about as simple as I could make it):
// #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 backing database just has a single table named [Number] with a single column [Value] INT NOT NULL.
As written, the program works for me, but uncommenting the #define at the top will switch the name of the method call and then the program will throw a NotSupportedException when executing ToList().
I've tried several different method names to try to determine a pattern, but have not been able to. It looks like if the method is named anything that starts with To it will throw the NotSupportedException, but seems to work with any other name. This is still odd.
Can someone explain what is happening?
Here's another sample without the #define toggle, it just does both methods back to back and I'm still seeing the same issue. Specifically, DoIt works, but ToValueType does not.
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,
}
}
}