We have some legacy code that uses Linq to SQL as the ORM. We\'d like to migrate this logic to .Net Core so that we can house it on a linux server. As far as I can tell, L2S is
If you used L2S because EF is inefficient at using Skip and Take for fetching large results as chunks, then your best bet is Dapper. Get yourself a copy of LINQPad and use it to get the generated SQL for each of your LINQ expressions.
L2S wraps some bizarre SQL around the actual query to use SQL's rownumber function to implement skip and take. If you are using the latest version of SQL Server then you don't need this because TSQL now has clauses equivalent to skip and take. This is handy if you are writing SQL directly and produces comprehensible SQL that won't induce WTF in those who follow, but the LINQ way works on all versions of SQL Server.
Then use this SQL with Dapper, which will do the ORM part for you. It also has proper support for type mapping parameters similar to L2S so you can avoid building SQL strings and injection vulnerabilities.
If you want all the smarts for constructing object graphs with FK values implied by collection membership then you're out of luck, you'll have to code it by hand.
EF is less horrible than it used to be. EF Core is simpler than EF while retaining many of the benefits. I am currently using EF Core on a project at work and it's not the disaster EF once was.
I did have to help with an outer join. Left to its own devices, LINQ fetched the inner part and then for each inner row ran a separate query for its outer portion.
I fixed this by explicitly fetching the inner part and constructing a keyset as an array of int. Another LINQ statement fetched all of the outer rows exploiting the fact that Array.Contains
maps to IN which uses indexes. Then I materialised both parts using ToArray()
and used LINQ to join them in memory. This brought execution time down from ten minutes to 300ms.
You shouldn't have to do this; L2S wouldn't have cocked it up in the first place. But at least there's a straightforward general solution.
A shortcoming of my solution is that it is not well adapted to progressive fetch.
Dapper can return dynamic results. I find this is a good bridge between SQL and C#. The columns and their names are governed by the SQL. This can be a little fragile since C# is case sensitive and SQL isn't, but with the SQL in your code you can at least see what it is and fix it.
More to the point, you can use LINQ directly on these dynamic results.