-
-
Save ravindUwU/41c1f0d6beaa7f2606c9fb846f0f6d99 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.EntityFrameworkCore; | |
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel.DataAnnotations.Schema; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Threading.Tasks; | |
[Table("entity")] | |
[PrimaryKey(nameof(Id))] | |
class Entity | |
{ | |
[Column("id")] | |
public required int Id { get; set; } | |
[Column("col_1")] | |
public required string Col1 { get; set; } | |
[Column("col_2")] | |
public required string Col2 { get; set; } | |
[Column("col_3")] | |
public required string Col3 { get; set; } | |
} | |
partial class Db : DbContext | |
{ | |
public DbSet<Entity> Entities { get; set; } = default!; | |
} | |
partial class Program | |
{ | |
static async Task Main() | |
{ | |
using var db = new Db(); | |
Write("EF ToDictionaryAsync - SELECTs all columns"); | |
await db.Entities.ToDictionaryAsync((e) => e.Id, (e) => e.Col1); | |
Write("ToDictionary2Async - SELECTs only Id & Col1"); | |
await db.Entities.ToDictionary2Async((e) => e.Id, (e) => e.Col1); | |
Write("ToDictionary2Async - SELECTs only Id & Col1"); | |
DbFunctions d = default!; | |
await db.Entities.ToDictionary2Async((e) => e.Id, (e) => d.IsNumeric(e.Col1)); | |
Write("ToDictionary2Async - SELECTs only Id, Col1 & Col3"); | |
await db.Entities.ToDictionary2Async((e) => e.Id, (e) => new { e.Col1, e.Col3 }); | |
} | |
} | |
public static class Extensions | |
{ | |
public static async Task<Dictionary<K, V>> ToDictionary2Async<T, K, V>( | |
this IQueryable<T> source, | |
Expression<Func<T, K>> keySelector, | |
Expression<Func<T, V>> valueSelector | |
) | |
where K : notnull | |
{ | |
return (await source | |
.Select(Combine(keySelector, valueSelector)) | |
.ToListAsync()) | |
.ToDictionary((kv) => kv.Key, (kv) => kv.Value); | |
} | |
class KV<K, V>(K key, V value) | |
{ | |
public K Key { get; } = key; | |
public V Value { get; } = value; | |
} | |
static Expression<Func<T, KV<K, V>>> Combine<T, K, V>( | |
Expression<Func<T, K>> keySelector, | |
Expression<Func<T, V>> valueSelector | |
) | |
{ | |
// Assuming keySelector is `(e1) => e1.K` and valueSelector is `(e2) => e2.V`, replace `e1` | |
// & `e2` with a shared parameter `t`, | |
var t = Expression.Parameter(typeof(T)); | |
var keyVisitor = new ChangeParam(from: keySelector.Parameters[0], to: t); | |
var newKeySelector = keyVisitor.VisitAndConvert(keySelector, nameof(Combine)); | |
// ...which is `(t) => t.K` | |
var valueVisitor = new ChangeParam(from: valueSelector.Parameters[0], to: t); | |
var newValueSelector = valueVisitor.VisitAndConvert(valueSelector, nameof(Combine)); | |
// ...which is `(t) => t.V` | |
// Return the expression `(t) => new KV(t.K, t.V)`, | |
return Expression.Lambda<Func<T, KV<K, V>>>( | |
body: Expression.New( | |
constructor: typeof(KV<K, V>).GetConstructor([typeof(K), typeof(V)])!, | |
arguments: [ | |
newKeySelector.Body, | |
newValueSelector.Body, | |
] | |
), | |
parameters: [t] | |
); | |
// ...EF core can translate it from here onward ^^ | |
} | |
class ChangeParam(ParameterExpression from, ParameterExpression to) : ExpressionVisitor | |
{ | |
protected override Expression VisitParameter(ParameterExpression param) | |
{ | |
return param == from ? to : base.VisitParameter(param); | |
} | |
} | |
} | |
partial class Db | |
{ | |
protected override void OnConfiguring(DbContextOptionsBuilder builder) | |
{ | |
builder.UseSqlServer(CONNECTION_STRING("localhost", "temp")); | |
builder.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information); | |
} | |
private string CONNECTION_STRING(string host, string db) | |
=> $"Server={host}; Database={db}; Integrated Security=True; TrustServerCertificate=True; Application Name=TESTCONSOLE;"; | |
} | |
partial class Program | |
{ | |
public static void Write(string m) | |
{ | |
var oldForeground = Console.ForegroundColor; | |
Console.ForegroundColor = ConsoleColor.Cyan; | |
Console.WriteLine($"\n{m}"); | |
Console.ForegroundColor = oldForeground; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
EF ToDictionaryAsync - SELECTs all columns | |
info: 27/08/2024 00:48:50.755 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) | |
Executed DbCommand (36ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] | |
SELECT [e].[id], [e].[col_1], [e].[col_2], [e].[col_3] | |
FROM [entity] AS [e] | |
ToDictionary2Async - SELECTs only Id & Col1 | |
info: 27/08/2024 00:48:50.927 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) | |
Executed DbCommand (7ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] | |
SELECT [e].[id], [e].[col_1] | |
FROM [entity] AS [e] | |
ToDictionary2Async - SELECTs only Id & Col1 | |
info: 27/08/2024 00:48:50.980 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) | |
Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] | |
SELECT [e].[id], CASE | |
WHEN ISNUMERIC([e].[col_1]) = 1 THEN CAST(1 AS bit) | |
ELSE CAST(0 AS bit) | |
END | |
FROM [entity] AS [e] | |
ToDictionary2Async - SELECTs only Id, Col1 & Col3 | |
info: 27/08/2024 00:48:50.989 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) | |
Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] | |
SELECT [e].[id], [e].[col_1], [e].[col_3] | |
FROM [entity] AS [e] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment