为什么LINQ-to-SQL有时允许我使用函数进行投影,但有时它不会?

Ant*_*ony 5 c# linq-to-sql

这真让我感到困惑.我知道LINQ-to-SQL处理选择是通过处理表达式树并尝试通过生成的查询来翻译东西,这就是为什么某些函数转换不起作用的属性(string.IsNullOrWhitespace经常是烦恼).

我遇到了我的代码中的情况,其中LINQ-to-SQL能够通过Select投影调用我的帮助函数...有时候.实际上,当方法有一个名称但它不起作用时它有另一个名称.我认为这可以通过以下程序得到最好的说明(这就像我可以做到的那样简单):

// #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,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

背数据库只是有一个名为一个表[Number]与单个列[Value] INT NOT NULL.

如上所述,该程序适用于我,但取消注释#define顶部将切换方法调用的名称,然后程序将NotSupportedException在执行时抛出ToList().

我已经尝试了几种不同的方法名称来尝试确定一种模式,但却无法做到.看起来如果方法被命名为任何以To它开头的东西将抛出NotSupportedException,但似乎与任何其他名称一起使用.这仍然很奇怪.

有人可以解释发生了什么吗?

这是另一个没有#define切换的示例,它只是两种方法背靠背,我仍然看到同样的问题.具体来说,DoIt有效,但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,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

usr*_*usr 4

这是一个 L2S 错误。预计这会起作用。

在反编译的源代码中我发现:

    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;
    }
Run Code Online (Sandbox Code Playgroud)

正在寻找"To"

if (mc.Method.Name.StartsWith("To", StringComparison.Ordinal))
Run Code Online (Sandbox Code Playgroud)

嗯……还有一个地方。也许 L2S 认为你的值类型是小数。尝试添加第二个参数来打破mc.Arguments.Count == 1条件。

无论如何,这是一个错误。这背后并没有什么更深层次的原因。通常,L2S 可以很好地执行最终 Select 中的函数。这是 EF 仍然缺乏的一个很棒的功能。

让它休息并尽快迁移到 EF。L2S 已被放弃。