Reputation: 83
I have a queryable of entities with a "Code" field against them
1.1.1,
1.1.2,
1.1.3,
1.1.4,
...,
1.1.10,
1.1.11
Unfortunately, When I do an .OrderBy(x=> x.Code)
on the query, it returns in the following order
1.1.1,
1.1.10,
1.1.11,
1.1.2,
1.1.3,
...
How can I make it that the object list is ordered by the code field, split by the "."s and as an integer between each part?
This is customer data, so I can't just put a "0" in front of the 1 number ones. Also it is any number of "."s in this Code field.
Let me know if you require further information.
Upvotes: 4
Views: 632
Reputation: 13393
If you can make some assumption like every node can have max n letter. you can use this code.
.OrderBy(x => String.Concat( x.Code.Split('.')
.Select(ss => ss.PadLeft(3, '0'))) )
Upvotes: 5
Reputation: 45947
The Version class should be a good approach for that. Unfortunately Version.Parse
fails at Linq2Entity so you have to fetch the data first from your sql-server and sort it afterwards.
var result = input.AsEnumerable<string>().OrderBy(x => Version.Parse(x.Code));
Upvotes: 5
Reputation: 101443
If you have non-trivial amount of data in your table - you cannot really use proposed solutions which require to pull all data from server and then sort on client. To do that on server and still using Entity Framework you need to either create SQL function or computable column and sort by that, unless you can figure out how to convert your code to sortable form using only functions translatable to SQL by Entity Framework. If you are using SQL Server, example of SQL function would be:
CREATE FUNCTION dbo.DotSeparatedSort(@input varchar(max))
RETURNS hierarchyid
AS
BEGIN
return CAST(N'/' + replace(@input, '.', '/') + N'/' AS hierarchyid)
END
Which will convert dot separated numbers to hierarchyid
type which has sorting semantics you need.
The same can be done with computed column (just put CAST(N'/' + replace(Code, '.', '/') + N'/' AS hierarchyid)
as formula).
Then you need to import function to EF model (details depend on which EF version you use and if that is code-first or database-first) and sort by that:
ctx.Entities.OrderBy(c => ctx.DotSeparatedSort(c.Code))
Which will be translated to select * from Entities order by dbo.DotSeparatedSort(Code)
. In case of computed column you obviously just sort by that.
If you use computed column - you can define index on it, so if you have really large amount of data - I'd prefer computed column because of that.
Upvotes: 1
Reputation: 5847
It's problematic to apply the correct sorting (OrderBy
) when executing as a Query (e.g. LINQ to Entities), because the expression is directly converted to a ORDER BY
SQL Expression, and there is no such SQL clause.
What you CAN do is execute the SQL without a particular sorting to materialize the data (e.g. run .ToList()
on it) and then sort the Data in Memory using this specific Sort Override.
This is how your custom Comparer implementation could look like (copied from above page and updated, left comments as is etc.):
public class CodeComparer : IComparer<MyEntity>
{
public int Compare(MyEntity x, MyEntity y)
{
if (x == null)
{
if (y == null)
{
// If x is null and y is null, they're
// equal.
return 0;
}
else
{
// If x is null and y is not null, y
// is greater.
return -1;
}
}
else
{
// If x is not null...
//
if (y == null)
// ...and y is null, x is greater.
{
return 1;
}
else
{
return ToComparableString(x.Code).CompareTo(ToComparableString(y.Code));
}
}
}
private string ToComparableString(string input)
{
var split = input.Split(new [] {'.'});
return string.Join(".", split.Select(x => x.PadLeft(5, '0')));
}
}
And then, you apply the Sorting:
var query = (...your Query or data source...).ToList();
var sortedList = query.Sort(new CodeComparer());
It will convert a string like "1.15.141" to "00001.00015.0141", making it comparable. (you could also remove the dot .
, also you can adjust the padding length to the expected maximum number, e.g. make it 2 instead of 5 if you have a maximum of 99 per "node".
Upvotes: 0