Skip to content

Instantly share code, notes, and snippets.

@habib-sadullaev
Last active December 12, 2023 10:17
Show Gist options
  • Save habib-sadullaev/54434a96902ef1e40673d483dec66f08 to your computer and use it in GitHub Desktop.
Save habib-sadullaev/54434a96902ef1e40673d483dec66f08 to your computer and use it in GitHub Desktop.
Dynamic dispatching LINQ for non-generic `IQueryable` and `LambdaExpression`
#r "nuget: FSharp.Interop.Dynamic"
open System
open System.Collections
open System.Linq.Expressions
open System.Linq
open FSharp.Interop.Dynamic
// query.Select(selector)
let select (selector: LambdaExpression) (query: IQueryable) : IQueryable =
Dyn.staticTarget<Queryable> |> Dyn.invokeGeneric "Select" [query.ElementType; selector.Body.Type] (query, selector)
// query.ToArray()
let toArray (query: IQueryable) : IEnumerable = Dyn.staticTarget<Enumerable> |> Dyn.invokeGeneric "ToArray" [query.ElementType] query
type Expression with static member Lambda(expression: Expression<Func<'T, 'TProp>>) = expression
type Foo() = member val Id = 0 with get, set
let selector : LambdaExpression = Expression.Lambda(fun (foo: Foo) -> foo.Id * 2)
let query : IQueryable = [for i in 1..5 -> Foo(Id = i)].AsQueryable()
query |> select selector |> toArray
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
LambdaExpression selector = (Foo foo) => foo.Id * 2;
IQueryable query = Enumerable.Range(1, 5).Select(x => new Foo { Id = x }).AsQueryable();
query.Select(selector).ToArray();
class Foo { public int Id {get;set;} }
public static class QueryableExtensions
{
// query.Select(selector)
public static IQueryable Select(this IQueryable query, LambdaExpression selector)
=> Queryable.Select((dynamic)query, (dynamic)selector);
// query.ToArray()
public static IEnumerable ToArray(this IQueryable query) => Enumerable.ToArray((dynamic)query);
}
@habib-sadullaev
Copy link
Author

habib-sadullaev commented Dec 11, 2023

The ? operand can be used to make static method calls a little easier.

#r "nuget: FSharp.Interop.Dynamic"

open System
open System.Collections
open System.Linq.Expressions
open System.Linq
open FSharp.Interop.Dynamic

module Query =
    let private staticInvocation target name typeArgs value   =
        Dyn.staticContext target |>  Dyn.invokeGeneric name typeArgs value 

    let private (?) target name = staticInvocation target name

    // query.Select(selector)
    let select (selector: LambdaExpression) (query: IQueryable) : IQueryable =
        typeof<Queryable>?Select [query.ElementType; selector.Body.Type] (query, selector)

    // query.ToArray()
    let toArray (query: IQueryable) : IEnumerable = typeof<Enumerable>?ToArray [query.ElementType] query

type Expression with static member Lambda(expression: Expression<Func<'T, 'TProp>>) = expression

type Foo() = member val Id = 0 with get, set
    
let selector : LambdaExpression = Expression.Lambda(fun (foo: Foo) -> foo.Id * 2)

let query : IQueryable = [for i in 1..5 -> Foo(Id = i)].AsQueryable()

query |> Query.select selector |> Query.toArray

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment