Entity Framework/Linq表达式将字符串转换为整数

25

我有一个表达式,如下所示:

var values = Enumerable.Range(1,2);

return message => message.Properties.Any(
    p => p.Key == name 
    && int.Parse(p.Value) >= values[0] 
    && int.Parse(p.Value) <= values[1]);

这个代码可以编译通过,但是当它到达数据库时会抛出异常'LINQ to Entities does not recognize the method 'Int32 Parse(System.String)' method, and this method cannot be translated into a store expression '

如果我不解析并使值成为一个string[],我就不能在字符串上使用>=<=运算符。

p.Value是一个包含各种值的字符串,在这种情况下它是int

是否有一种方法可以查询数据库以进行这种between语句?


4
你认为这不是一个警示信号吗?意味着你的数据库中"Value"应该是一个整数类型。 - Mathew Thompson
啊,得爱上“内部平台效应”……使用数据库表来构建另一个数据库表(列/行)…… - Marc Gravell
Convert.ToInt32(p.Value) 能够正常工作吗? - Marc Gravell
3
请使用Convert.ToInt32。EF可以理解这个方法。 - vcsjones
12
不行,我试过了。 - Jon
显示剩余2条评论
5个回答

20
尽管我很讨厌这个答案,但实际上你不能轻易地做到这一点。它将是一个真正的痛苦。 我看到了很多错误的答案和很多人说你应该在第一次就把数据库字段设置为正确的类型,这是没有帮助的。
你的问题类似于MSDN上的这个问题。
根据你使用的EF类型,有几种可能性。
1. 你正在使用EDMX文件(而不是代码优先或反向工程代码优先)。
你可以使用像这样的东西(从这个答案中):
[EdmFunction("PlusDomain", "ParseDouble")]
public static double ParseDouble(string stringvalue)
{
    // This method exists for use in LINQ queries,
    // as a stub that will be converted to a SQL CAST statement.
    return System.Double.Parse(stringvalue);
}

将其映射到EDMX中,如下所示:
<Function Name="ParseDouble" ReturnType="Edm.Double">
    <Parameter Name="stringvalue" Type="Edm.String" />
    <DefiningExpression>
        cast(stringvalue as Edm.Double)
    </DefiningExpression>
</Function>

2. 如果你在EF中使用了Code First >= 4.1。

那么你可能会遇到麻烦。微软没有在SqlFunctions中添加任何此类函数。你最好的希望是将一个标量SQL函数添加到你的数据库中,然后(也许)尝试将其映射到你的上下文中。微软认为在Code First中不需要做任何这样的事情,但幸运的是他们也没有完全阻止这样的事情发生。Fluent API功能强大,你可以像这样调用函数或存储过程(参考):

var outParam = new SqlParameter("overHours", SqlDbType.Int);
outParam.Direction = ParameterDirection.Output;

或者像这样(参考):

var data = context.Database.ExecuteSqlCommand("dbo.sp_getNumberJobs @overHours OUT", outParam);
int numJobs = (int)outParam.Value;

但要使它们真正集成到LINQ to Entities中,您需要使用类似 CodeFirstFunctions 的东西,通过使用 EntityFramework.CodeFirstStoreFunctions NuGet软件包来实现。它将SQL功能映射到上下文中,但它仅适用于 .NET 4.5的外部库(请参见这里)。

相反,您可以尝试手动完成与 此问题 中所述的方式相同的操作。

我为自己的需求选择的更快速的解决方案是创建一个具有转换类型的视图。这避免了整个问题。


1
我知道这是一个旧答案,但它在最近的问题上让我眼前一亮。这个答案很好,但也有一种方法可以使用模型定义的函数与代码优先!不过需要更多的工作。请参见https://dev59.com/Monca4cB1Zd3GeqP_XeY。此外,MS代码似乎会在实际的C#代码中抛出NotImplementedException("not for direct calls"),我认为这是明智的。该调用应该从表达式树转换为实际的SQL,而不是直接调用! - Alexandru Clonțea

8

正如其他评论中指出的那样,你需要解析这个值,这应该是一个红旗,提示你应该在数据库中使用不同的数据类型。

幸运的是,有一种解决方法可以强制查询由LINQ to Objects执行而不是由LINQ to Entities执行。不幸的是,这意味着可能要将大量数据读入内存中。

编辑

根据您的其他评论,Value列中的值不能保证是数字。因此,您需要尝试将该值转换为数字,然后根据转换的成功/失败来处理事情:

return message
       .Properties
       .AsEnumerable()
       .Any(p => 
            {
                var val = 0;
                if(int.TryParse(p.Value, out val))
                {
                    return p.Key == name &&
                           val >= values[0] &&
                           val <= values[1])
                }
                else
                {
                    return false;
                }
           );

编辑2

你实际上可以在数据库中实现这个功能。我不确定这对你是否有效,但试一试吧:

return message.Properties
              .Where(p => p.Key == name && SqlFunctions.IsNumeric(p.Value) > 0)
              .Any(p => Convert.ToInt32(p.Value) >= values[0] &&
                        Convert.ToInt32(p.Value) <= values[1]);

我正在尝试避免这样做,因为这是筛选方法的一部分,应该通过访问数据库并返回筛选后的数据来实现。 - Jon
@Jon - 由于数据不一定是数字,因此在数据库中处理转换没有好的方法。您可以使用类似上述代码的代码,或者使用字符串进行比较。 - Justin Niessner
4
我遇到了问题,LINQ to Entities不认识方法 'Int32 ToInt32(System.String)',而且这个方法无法被转换成存储表达式。 - Jon
@Jon - 你在使用EDMX还是Code-First来进行EF开发? - Justin Niessner
1
你的第二次编辑在7年后挽救了我的生命。谢谢! - Karel Křesťan
显示剩余4条评论

1

最近我需要在LINQ查询中将一个字符串(数字表示)转换为枚举值,而不需要实例化查询。我的枚举使用了0至9之间的int值,因此我最终做了这样的事情:

    .Select(str => (MyEnum?)(SqlFunctions.Unicode(str) - 48))

我知道这并不能提供完整的解决方案,但在像我这样的情况下,它绝对有效。也许有人会觉得它有用。

0
你可以使用 SqlFunctions.IsNumeric() 方法,例如将其存储到列表中。
var resultsArray = context.TableData_DB.Where(x => SqlFunctions.IsNumeric(x.stringField) >= -1).ToList();

1
已经在这里回答了。此外,>= -1 是不正确的。 - Gert Arnold

-4
你可以尝试使用 Convert.ToInt32(input); 如果不确定,可以使用 TryParse。它不会抛出异常,但是如果无法解析则返回 false。但是 Convert.ToInt32(input); 应该可以正常工作。你可以在 http://msdn.microsoft.com/en-us/library/vstudio/bb397679.aspx 上了解更多信息。

1
在 EF 查询表达式中不能使用 TryParse - Eren Ersönmez
没有立即检查它的机会,但是谢谢你!我认为它可能有效。 - user2399760

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接