将字符串排序为linq中的数字到实体

时间:2014-09-30 08:33:37

标签: c# linq linq-to-entities

我有som字符串,如“1”,“2”,“3”,“10”等,当使用orderby排序列表是“1”,“10”,“2”,“3”时。我想把它们分类为1,2,3,...,10。我使用下面的代码对列表进行排序。

var model = (from c in General.db.GlbTbComboBases
             where c.ClassCode.Equals(classCode)
             select new ReturnData { id = c.BaseCode, name = c.FAName }).OrderBy(c => c.id,
             new SemiNumericComparer());
             if (model.Any())
            {
                CacheManager.cache.GetOrAdd<List<ReturnData>>(key, () => 
                model.ToList<ReturnData>());
                return model.ToList<ReturnData>();
            }

public class SemiNumericComparer : IComparer<string>
        {
            public int Compare(string s1, string s2)
            {
                if (IsNumeric(s1) && IsNumeric(s2))
                {
                    if (Convert.ToInt32(s1) > Convert.ToInt32(s2)) return 1;
                    if (Convert.ToInt32(s1) < Convert.ToInt32(s2)) return -1;
                    if (Convert.ToInt32(s1) == Convert.ToInt32(s2)) return 0;
                }

                if (IsNumeric(s1) && !IsNumeric(s2))
                    return -1;

                if (!IsNumeric(s1) && IsNumeric(s2))
                    return 1;

                return string.Compare(s1, s2, true);
            }

            public static bool IsNumeric(object value)
            {
                try
                {
                    int i = Convert.ToInt32(value.ToString());
                    return true;
                }
                catch (FormatException)
                {
                    return false;
                }
            }
        }

当我运行代码时出现此错误:

LINQ to Entities does not recognize the method 'System.Linq.IOrderedQueryable`1[Salary.Classes.ReturnData] OrderBy[ReturnData,String](System.Linq.IQueryable`1[Salary.Classes.ReturnData], System.Linq.Expressions.Expression`1[System.Func`2[Salary.Classes.ReturnData,System.String]], System.Collections.Generic.IComparer`1[System.String])' method, and this method cannot be translated into a store expression.

这是一个遗留数据库,我无法更改任何数据类型,因为可能会增加其他应用程序的错误。

4 个答案:

答案 0 :(得分:3)

这里有两个问题:

  1. 您将数字存储为数据库中的字符串
  2. 您尝试在Sql Server上执行C#代码
  3. 您收到的异常是由于编译器无法将比较逻辑从SemiNumericComparer类转换为sql查询。

    为了达到理想的效果,您可以:

    a)将所有数据加载到内存中,并使用内存中的SemiNumericComparer执行比较,方法是迭代选定的结果并在此之后对它们进行排序:

    var model = (from c in General.db.GlbTbComboBases
                 where c.ClassCode.Equals(classCode)
                 select new ReturnData { id = c.BaseCode, name = c.FAName })
                .ToList() // this will load data into memory
                .OrderBy(c => c.id, new SemiNumericComparer());
    

    然而,这不是一个好方法,因为如果数据集非常小,它将增加大量无用的内存消耗,如果您的数据集在给定时间大于可用内存,则会使应用程序崩溃。

    b)使用SqlFunctions在Sql Server上将字符串转换为数字,并使用Sql Server提供的顺序将它们命名为数字:

    var model = (from c in General.db.GlbTbComboBases
                 where c.ClassCode.Equals(classCode)
                 select new ReturnData { id = c.BaseCode, name = c.FAName })
                .OrderBy(c => SqlFunctions.IsNumeric(c.id));
    

答案 1 :(得分:0)

如果您需要数字订单,则必须对数字数据类型进行排序:

    string[] str = {"1", "10", "2","011"};

    List<string> ordered = str.OrderBy(x => int.Parse(x)).ToList();

答案 2 :(得分:0)

尝试这样的事情:

var model = (from c in General.db.GlbTbComboBases
         where c.ClassCode.Equals(classCode)
         select new { Id = Convert.ToInt32(c.BaseCode), Name = c.FAName })
         .OrderBy(c => c.Id)
        .Select(x => new ReturnData { id = x.Id, name = x.Name });

只需添加一个匿名类型进行排序,然后转换为所需类型。当然需要更多的记忆。

答案 3 :(得分:0)

在 linq to entity 上没有将字符串转换为 int 的内置方法。 SqlFunctions 类有 many useful functions 但没有一个支持将字符串转换/解析/转换为 int。

您可以使用自己的自定义 sql 函数,然后将其添加到您的 EF 模型中并根据需要使用它。我用表值函数测试过,不知道是否也可以用标量值函数。

CREATE FUNCTION [dbo].[StringToInt](
    @strInt AS VARCHAR(30)
)
RETURNS TABLE WITH SCHEMABINDING
RETURN
    SELECT TRY_CAST (@strInt AS INT) [IntValue];

然后将其添加到 EF 模型 (YourModel.context.cs)

[DbFunction("YourContext", "StringToInt")]
public virtual IQueryable<Nullable<int>> StringToInt(string strInt)
{
    var strIntParameter = strInt != null ?
        new ObjectParameter("strInt", strInt) :
        new ObjectParameter("strInt", typeof(string));

    return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<Nullable<int>>("[YourContext].[StringToInt](@strInt)", strIntParameter);
}

最后,您可以通过这种方式在排序表达式中使用它:

var qry = context.SomeEntityYouHave
            .Where(x => x.Category == "Numbers")
            .OrderBy(
                x => ctx.StringToInt(x.StringPropertyContainingNumbersToSort)
                .FirstOrDefault()//Don't forget to call FirstOrDefault
            );

通过这种方法,您可以在 SQL 端对数据进行排序,从而无需提前调用 .ToList(),然后对应用程序内存中的所有数据进行排序。 当然,将 int 值存储为 int 而不是字符串是更好的方法,但有时由于不同的原因,它不能成为一种选择。