我有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.
这是一个遗留数据库,我无法更改任何数据类型,因为可能会增加其他应用程序的错误。
答案 0 :(得分: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 而不是字符串是更好的方法,但有时由于不同的原因,它不能成为一种选择。