Skip to content

Instantly share code, notes, and snippets.

@ravindUwU
Last active August 27, 2024 01:24
Show Gist options
  • Save ravindUwU/41c1f0d6beaa7f2606c9fb846f0f6d99 to your computer and use it in GitHub Desktop.
Save ravindUwU/41c1f0d6beaa7f2606c9fb846f0f6d99 to your computer and use it in GitHub Desktop.
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;
}
}
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