Skip to content

Feature request - Add an extension point to process expressions built at runtime within the DD process chain #297

@magicmoux

Description

@magicmoux

Hi @hazzik,

following on my last issue, I think it would be nice to provide an Extension point be able to include runtime built LINQ Extensions seamlessly into the DD pipeline.

So I POCed this fork's branch by adding a new [ExpressionFactory] attribute that allows you to define methods that generate expression trees dynamically at runtime during the decompilation phase. These custom operations integrate naturally into the LINQ query pipeline.

Direct usage and computed property patterns are demonstrated in the test group 91

Here's a quick overview of the design

Define an Expression Factory

Mark your factory method with [ExpressionFactory]:

[ExpressionFactory]
private static Expression BuildOrderBySequenceExpression<T, TKey>(
    Expression source, 
    Expression<Func<T, TKey>> keySelector, 
    IEnumerable<TKey> explicitSequence)
    where T : class
    where TKey : IEquatable<TKey>
{
    // Build expression tree dynamically based on runtime data
    // This is evaluated during query decompilation
}

Create the Public Extension Methods

Add a public extension method marked with [Decompile]:

[Decompile]
public static IOrderedQueryable<T> OrderBySequence<T, TKey>(
    this IQueryable<T> source, 
    Expression<Func<T, TKey>> keySelector, 
    IEnumerable<TKey> explicitSequence)
    where T : class
    where TKey : IEquatable<TKey>
{
    return (IOrderedQueryable<T>)source.Provider.CreateQuery(
        BuildOrderBySequenceExpression(source.Expression, keySelector, explicitSequence));
}

[Decompile]
// this extension allows to work also on nested navigation properties collections
public static IList<T> OrderBySequence<T, TKey>(this IEnumerable<T> source, IEnumerable<TKey> explicitSequence, Expression<Func<T, TKey>> matchSelector)
    where T : class
    where TKey : IEquatable<TKey>
{
    return (IList<T>)BuildOrderBySequenceExpression(() => source, explicitSequence, matchSelector);
}

You can now use it directly into your queries

var priorityOrder = new[] { 3, 1, 2 };
var results = queryableSource
    .OrderBySequence(o => o.Priority, priorityOrder)
    .ToList();

The expression tree is built at runtime based on the actual sequence values coming either from a local variable or function/contextual parameters.
Both produce equivalent expression trees, but the second approach is cleaner and reusable across the codebase.

What Changes ?

  • new [ExpressionFactory] Attribute - Marks methods as expression tree factories
  • new ExpressionFactoryVisitor - Processes factory methods during the decompilation pipeline to revert expressions into the actual arguments expected by the ExpressionFactory (this part is still in progress)
  • new Build() Extension Method - Orchestrates the complete decompilation pipeline
  • DecompiledQueryProvider - Now uses Build() for complete expression processing
  • DecompiledQueryable - Enumeration logic updated to use Build()

Use Cases

This feature would be particularly valuable for:

  • Complex filtering/ordering patterns with variable runtime criteria
  • Domain-specific query operations
  • Reusable query components that adapt to runtime data

There is no breaking changes since this is a purely additive feature. All existing code continues to work without modification.

Do you think this could be added to a future release ? Any insight is welcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions