为什么LINQ to SQL有时允许我使用函数进行项目,但有时却不允许

本文关键字:项目 不允许 to LINQ SQL 允许我 为什么 函数 | 更新日期: 2023-09-27 18:28:54

这真的让我很困惑。我知道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,
        }
    }
}

后台数据库只有一个名为[Number]的表和一列[Value] INT NOT NULL

正如所写的,该程序对我来说是有效的,但取消对顶部#define的注释将切换方法调用的名称,然后该程序在执行ToList()时将抛出NotSupportedException

我尝试了几个不同的方法名称来尝试确定模式,但都无法确定。看起来,如果该方法的名称以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,
        }
    }
}

为什么LINQ to SQL有时允许我使用函数进行项目,但有时却不允许

这是一个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;
    }

搜索"To":

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

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

不管怎样,这是一个bug。这背后没有更深层次的原因。通常情况下,L2S可以在最后的Select中执行函数,这很好。这是EF仍然缺乏的一个令人敬畏的功能。

将其搁置并尽快迁移到EF。L2S被放弃。