2

I have some Ids store in below variable:

List<int> Ids;

Now I want to get records based on above Ids but with same order as it is in above Ids.

For eg: Records are like this in database:

Employee:

Id
1
2
3
4
5

Now if Ids array holds Ids like this : 4,2,5,3,1 then I am trying to get records in this order order only:

Query:

  var data = context.Employee.Where(t => Ids.Contains(t.Id)).ToList();

But above query is giving me output like it is in table:

Id
1
2
3
4
5

Expected output :

Id
4
2
5
3
1

Update:I have already tried this below solution but as this is entity framework it didn't work out:

var data = context.Employee.Where(t => Ids.Contains(t.Id))
                    .OrderBy(d => Ids.IndexOf(d.Id)).ToList();

For above solution to make it working I have to add to list :

var data = context.Employee.Where(t => Ids.Contains(t.Id)).ToList()
                    .OrderBy(d => Ids.IndexOf(d.Id)).ToList();

But I don't want to load data in memory and then filter out my record.

halfer
  • 19,824
  • 17
  • 99
  • 186
I Love Stackoverflow
  • 6,738
  • 20
  • 97
  • 216
  • Some effort of you is required as well. Please read [ask] and share your research. This question has been asked plenty of times before, see for example the [duplicate](http://stackoverflow.com/questions/15275269/sort-a-list-from-another-list-ids). – CodeCaster Sep 06 '16 at 12:52
  • @CodeCaster I cant find Indexof method for Ids.so how this question is duplicate for you? – I Love Stackoverflow Sep 06 '16 at 12:54
  • @CodeCaster Indexof method will not gonna work for entity framework so before marking question as duplicate blindly please read question carefully.thanks – I Love Stackoverflow Sep 06 '16 at 12:57
  • Add a .ToList() then. Again, search and share your research, this question is not unique. – CodeCaster Sep 06 '16 at 13:07
  • @CodeCaster Please see my updated question – I Love Stackoverflow Sep 06 '16 at 15:23
  • The latter is not filtering in-memory, it's only sorting in-memory. – CodeCaster Sep 06 '16 at 15:28
  • @CodeCaster Yeah you are right and i was just avoiding that – I Love Stackoverflow Sep 06 '16 at 15:44
  • 1
    The better practice is to use `.AsEnumerable()` opposed to `.ToList()` to stop processing on the database-side and continue on the in-memory side. The latter immediately executes the query and creates an `List` while the former is still a query with deferred execution and all that. It just stops adding things to the SQL query, and performs it afterwards in-memory. – Maarten Sep 07 '16 at 07:56
  • @Maarten Point to be noted.thank you so much for suggestion :) – I Love Stackoverflow Sep 07 '16 at 08:01

2 Answers2

2

Since the order in which the data is returned when you do not specify an ORDER BY is not determined, you have to add an ORDER BY to indicate how you want it sorted. Unfortunately you have to order based on objects/values in-memory, and cannot use that to order in your SQL query.

Therefore, the best you can do is to order in-memory once the data is retrieved from the database.

var data = context.Employee
    // Add a criteria that we only want the known ids
    .Where(t => Ids.Contains(t.Id))
    // Anything after this is done in-memory instead of by the database
    .AsEnumerable()
    // Sort the results, in-memory
    .OrderBy(d => Ids.IndexOf(d.Id))
    // Materialize into a list
    .ToList();
Community
  • 1
  • 1
Maarten
  • 22,527
  • 3
  • 47
  • 68
1

Without stored procedures you can use Union and ?: that are both canonical functions.
I can't immagine other ways.

?:
You can use it to assign a weigth to each id value then order by the weigth. Also, you have to generate ?: using dynamic linq.

What is the equivalent of "CASE WHEN THEN" (T-SQL) with Entity Framework?
Dynamically generate LINQ queries

Union
I think this is the more simple way to obtain it. In this case you can add a Where/Union for each Id.

EDIT 1
About using Union you can use code similar to this

IQueryable<Foo> query = context.Foos.AsQueryable();
List<int> Ids = new List<int>();
Ids.AddRange(new[] {3,2,1});
bool first = true;
foreach (int id in Ids)
{
    if (first)
    {
        query = query.Where(_ => _.FooId == id);
        first = false;
    }
    else
    {
        query = query.Union(context.Foos.Where(_ => _.FooId == id));
    }
}


var results = query.ToList();

This generate the followiong query

SELECT
[Distinct2].[C1] AS [C1]
FROM ( SELECT DISTINCT
        [UnionAll2].[C1] AS [C1]
        FROM  (SELECT
                [Distinct1].[C1] AS [C1]
                FROM ( SELECT DISTINCT
                        [UnionAll1].[FooId] AS [C1]
                        FROM  (SELECT
                                [Extent1].[FooId] AS [FooId]
                                FROM [Foos] AS [Extent1]
                                WHERE [Extent1].[FooId] = @p__linq__0
                        UNION ALL
                                SELECT
                                [Extent2].[FooId] AS [FooId]
                                FROM [Foos] AS [Extent2]
                                WHERE [Extent2].[FooId] = @p__linq__1) AS [UnionAll1]
                )  AS [Distinct1]
        UNION ALL
                SELECT
                [Extent3].[FooId] AS [FooId]
                FROM [Foos] AS [Extent3]
                WHERE [Extent3].[FooId] = @p__linq__2) AS [UnionAll2]
)  AS [Distinct2]
p__linq__0 = 3
p__linq__1 = 2
p__linq__2 = 1

EDIT 2
I think the best approach is in memory approach because it has the same network load, EF does not generate the ugly query that could not work on databases different from SQL Server and code is more readable. In your particular application could be that union/where is better. So, generally I would suggest you to try memory approach then, if you have [performance] issues, you can check if union/where is better.

Community
  • 1
  • 1
bubi
  • 6,414
  • 3
  • 28
  • 45
  • Can you please provide any example of how i would use union in this case to achieve desired result??? – I Love Stackoverflow Sep 07 '16 at 06:19
  • See EDIT above. – bubi Sep 07 '16 at 06:37
  • I really appreciate your answer that is why i have upvoted but the only concern is that which approcah will be better.In memory approach or your approach??? – I Love Stackoverflow Sep 07 '16 at 07:07
  • 1
    This is only an answer to your question. I think the better approach is in memory approach (I use it in my apps) because it has the same network load, EF does not generate the ugly query that could not work on databases different from SQL Server and code is more readable. In your particular application could be that union/where is better. So, generally I would suggest you to try memory approach then, if you have [performance] issues, you can check if union/where is better. – bubi Sep 07 '16 at 07:49
  • Thank you so much sir for your kind help and please keep helping like this :) – I Love Stackoverflow Sep 07 '16 at 12:32