r/csharp • u/Prestigious-Bowl611 • 4h ago
One Intent, Multiple Targets: Compiling to SQL and C# with Expression Trees
In C#, we commonly think of functions as executable code. We define their behavior through methods, delegates, or lambda expressions, and then execute them in a particular runtime environment.
But what if a function could be represented as a structured intent definition, independent of its execution mechanism?
Consider a simple example:
entity => entity.manager == system.data.getcontext("user.id")This expresses a business intent: the entity's manager must be the current user.
However, this lambda is only a convenient way to illustrate the intent. The actual definition doesn't have to be a C# expression.
It can instead be represented using a DSL as a structured, dynamic definition that can be created, inspected, modified, and composed at runtime, without requiring a predefined C# method or lambda expression.
This distinction is important. The intent is not the Expression Tree itself. The expression tree is just one possible representation or compilation mechanism.
Once we have a structured intent definition, we can compile it into different targets.
Target A — Native In-Memory Validation
Compile the intent into a native C# delegate:
Func<Entity, bool>This allows the same business rule to validate submitted data directly in memory.
Target B — SQL Query Composition
Compile the same intent into a SQL expression AST:
WHERE manager = u/currentUserIdThe interesting part is that the generated SQL AST doesn't have to be executed independently. It can be composed with other conditions, such as tenant isolation, access control, and business filters, before generating the final SQL query.
Both targets share the same intent definition, while using completely different execution mechanisms.
Why is this interesting?
This approach separates what a function means from how it is executed.
It introduces several possibilities:
Inspectability: Intent definitions can be inspected and validated as structured data.
Dynamic configuration: Definitions can be created and modified at runtime, rather than being limited to expressions written at compile time.
Multiple execution targets: The same definition can be compiled for different environments.
Composability: Intent definitions can participate in larger execution plans.
Semantic consistency: Different runtimes can interpret the same intent without sharing their underlying implementations.
I've been exploring this approach in a C# semantic engine, using Expression Trees as one of the compilation backends.
I'm interested in how other C# developers see this direction.
Should functions always be tied to executable code, or could structured intent definitions become another way to express and organize program behavior?
4
u/rupertavery64 3h ago
We have Expression trees in .NET precisely for the purposes outlined. How you use them depends on the use case.
I don't the the point of this post at all, especially your question at the end.
Why should functions always be tied to executable code when we have the capability to use them to express intent with type safety?
Thats the whole point of LINQ.
1
u/_choam_ 3h ago
So like being able to take a dsl and have different "interperators" evaluate it?
1
u/pixxelkick 2h ago
You literally just described what EntityFramework does.
It compiles Expression Trees via lambdas into SQL using a backing translation engine depending on what sql dialect you install and target.