DEV Community

Cover image for How to Perform Dynamic Code Execution in .NET with C# Eval Expression
Anton Martyniuk
Anton Martyniuk

Posted on • Originally published at antondevtips.com on

How to Perform Dynamic Code Execution in .NET with C# Eval Expression

Have you ever worked on an email template with placeholders that need to be replaced dynamically based on a set of values?
Or perhaps you've had to build a dynamic C# or LINQ expression for EF Core to filter and sort based on any attributes of the entity?

Today I want to introduce you to a library that I find incredibly helpful for these scenarios.

This library is C# Eval Expression, one of the most powerful and flexible expression evaluators you can find.

Let's dive in and see how it can simplify your development workflow.

On my website: antondevtips.com I share .NET and Architecture best practices.
Subscribe to advance your career in .NET.
Download the source code for this blog post for free.

Dynamic Email Text Templating with C# Eval Expression

To get started with C# Eval Expression, install the following Nuget package:

dotnet add package Z.Expressions.Eval
Enter fullscreen mode Exit fullscreen mode

C# Eval Expression is compatible with .NET, .NET Core and .NET Framework:

  • .NET 5+
  • .NET Framework 4.0+

C# Eval Expression allows you to evaluate and execute C# code on the fly.

A common use case is generating personalized email content by replacing placeholders with model values in a template.
With C# Eval Expression, you can easily parse and render templates at runtime.

Let's explore an example. Consider the following model definitions:

public record Customer
{
    public string Name { get; init; } = null!;
    public List<OrderItem> Items { get; init; } = [];
}

public record OrderItem
{
    public string Name { get; init; } = null!;
    public decimal Price { get; init; }
    public int Quantity { get; init; }
}
Enter fullscreen mode Exit fullscreen mode

Here is a basic email template:

public string GetEmailTemplate()
{
    return @"
Hello {{Name}},

Thank you for your order.

Best regards,
The Team
";
}
Enter fullscreen mode Exit fullscreen mode

Using Eval.Execute, you can pass a Customer object to dynamically generate the email text:

var emailTemplate = GetEmailTemplate();
var emailText = Eval.Execute<string>($"return $$'''{emailTemplate}'''", customer);

Console.WriteLine(emailText);
Enter fullscreen mode Exit fullscreen mode

This produces the following output:

Hello Jermaine,

Thank you for your order.

Best regards,
The Team
Enter fullscreen mode Exit fullscreen mode

Now, let's enhance the template to include a table of order items.
C# Eval allows embedding C# code (as string) to build an HTML table dynamically:

var emailTemplate = GetEmailTemplate();
var emailText = Eval.Execute<string>($"return $$'''{emailTemplate}'''", customer);

public string GetEmailTemplate()
{
    return @"
Dear {{Name}},

Thank you for your recent order with us! We are currently processing the following items:

<table>
<tr><td>Product Name</td><td>Price ($)</td><td>Quantity</td></tr>
{{
    var sb = new StringBuilder();
    foreach(var item in Items) {
        sb.AppendLine($$""""""<tr><td>{{item.Name}}</td><td>${{item.Price}}</td><td>{{item.Quantity}}</td></tr>"""""");
    }
    return sb.ToString();
}}
</table>

We will notify you once your order is shipped.

Best regards,
The Customer Service Team
";
}
Enter fullscreen mode Exit fullscreen mode

In this template we are using a string with a C# code that creates a StringBuilder to append a table with order items.
When executed, the template will produce the following content:

Dear Arnold,

Thank you for your recent order with us! We are currently processing the following items:

<table>
<tr><td>Product Name</td><td>Price ($)</td><td>Quantity</td></tr>
<tr><td>Tasty Plastic Cheese</td><td>$41,99</td><td>1</td></tr>
<tr><td>Awesome Fresh Computer</td><td>$402,75</td><td>8</td></tr>
<tr><td>Ergonomic Concrete Shirt</td><td>$344,47</td><td>5</td></tr>
<tr><td>Rustic Plastic Gloves</td><td>$905,35</td><td>9</td></tr>
<tr><td>Ergonomic Steel Salad</td><td>$410,66</td><td>5</td></tr>

</table>

We will notify you once your order is shipped.

Best regards,
The Customer Service Team
Enter fullscreen mode Exit fullscreen mode

It looks fantastic, right?
You can experiment with a similar example on DotNetFiddle.

The C# Eval Expression library supports nearly everything from basic keywords to more advanced usage of the C# language, including:

  • Anonymous Type
  • Extension Method
  • Lambda Expression
  • LINQ Methods
  • Method Overloads

Executing LINQ Dynamically

Dynamic LINQ queries are another powerful use-case for C# Eval Expression.

Imagine a scenario where you need to filter data based on user input at runtime.
For example, consider filtering a list of products based on price.

We will define a filtering condition in a string "products.Where(x => x.Price > greaterThan)":

var products = new List<Product>
{
    new(1, "Laptop", 1200m),
    new(2, "Smartphone", 800m),
    new(3, "Tablet", 400m)
};

var greaterThan = 500m;

var filteredProducts = Eval.Execute<List<Product>>("products.Where(x => x.Price > greaterThan)",
    new { products, greaterThan });

foreach (var product in filteredProducts)
{
    Console.WriteLine(product);
}
Enter fullscreen mode Exit fullscreen mode

With Eval.Execute we can dynamically execute this LINQ expression.
The result will return 2 products that have price bigger than 500m:

Product { Id = 1, Name = Laptop, Price = 1200 }
Product { Id = 2, Name = Smartphone, Price = 800 }
Enter fullscreen mode Exit fullscreen mode

By constructing your LINQ queries dynamically, you gain flexibility in building data-intensive applications, such as those using EF Core.

Executing Dynamic LINQ Expressions on EF Core

Consider the following Author and the Book entities:

public class Author
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public List<Book> Books { get; set; } = [];
}

public class Book
{
    public Guid Id { get; set; }
    public string Title { get; set; }
    public int Year { get; set; }

    public Guid AuthorId { get; set; }
    public Author Author { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

And a corresponding DbContext:

public class ApplicationDbContext : DbContext
{
    public DbSet<Author> Authors { get; set; } = null!;
    public DbSet<Book> Books { get; set; } = null!;

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
    }
}
Enter fullscreen mode Exit fullscreen mode

I use it with SQLite database:

var connectionString = builder.Configuration.GetConnectionString("Sqlite");

builder.Services.AddDbContext<ApplicationDbContext>(x => x.EnableSensitiveDataLogging()
    .UseSqlite(connectionString));
Enter fullscreen mode Exit fullscreen mode

Now, let's build a dynamic LINQ query for EF Core.
For instance, filtering books with the title "Mouse" and sorting by the author's name in descending order can be done as follows:

var result = await dbContext.Books
    .Include(x => x.Author)
    .WhereDynamic(x => "x.Title == \"Mouse\")
    .OrderByDescending(x => "x.Author.Name")
    .ToListAsync();

result = await dbContext.Books
    .Include(x => x.Author)
    .WhereDynamic("x => x.Title == \"Mouse\")
    .OrderByDescendingDynamic("x => x.Author.Name")
    .ToListAsync();
Enter fullscreen mode Exit fullscreen mode

Here I specify a WhereDynamic and OrderByDescendingDynamic LINQ statements and passing expressions as strings inside.

Notice that two types of syntax are supported: where you specify the part of the lambda and the full lambda as string expression.

You can also create a more advanced filters like getting all Books of year 2020 and newer, that have Chips in their Title:

var result = await dbContext.Books
    .WhereDynamic(x => "x.Year >= 2020 && x.Title.Contains('Chips')")
    .ToListAsync();
Enter fullscreen mode Exit fullscreen mode

We can expand this example further by introducing a Minimal API endpoint that accepts a dynamic filtering and sorting expressions from the frontend:

app.MapPost("/api/books", async (
    ApplicationDbContext dbContext,
    BookFilterRequest request) =>
{
    var query = dbContext.Books
        .Include(x => x.Author)
        .AsQueryable();

    if (!string.IsNullOrWhiteSpace(request.Filter))
    {
        query = query.WhereDynamic(request.Filter);
    }

    if (!string.IsNullOrWhiteSpace(request.Sort))
    {
        query = query.OrderByDynamic(request.Sort);
    }

    var books = await query.ToListAsync();
    return Results.Ok(books);
});
Enter fullscreen mode Exit fullscreen mode

Let's send the following request:

{
  "filter": "x => x.Title == \"Mouse\" && x.Year >= 2020",
  "sort": "x => x.Author.Name"
}
Enter fullscreen mode Exit fullscreen mode

And pause the debugger:

Screenshot_1

As you can see, these string dynamic expressions have "magically" been translated into a valid SQL query.
It's truly fascinating how these dynamic strings are converted under the hood.

Feel free to play around with a similar example on DotNetFiddle.

Injecting C# Code Dynamically To Modify Application Behaviour

C# Eval can be used to safely inject and execute C# custom code into your application at runtime.
This is particularly useful in scenarios where you want to adjust UI elements, change business logic, or enable advanced configuration options without redeploying your application.

The key is the EvalContext class that requires you to register any types, methods, or variables that the injected code might use.

Consider the following model definitions:

public class PluginModel
{
    public User User { get; set; }
    public string Title { get; set; }
    public string H1 { get; set; }
    public string Footer { get; set; }
    public TextBox TextBox1 { get; set; } = new();
    public TextBox TextBox2 { get; set; } = new();
}

public class User
{
    public string Name { get; set; }
    public bool IsAdmin { get; set; }
}

public class TextBox
{
    public string Label { get; set; }
    public int MaxLength { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

The following script customizes the PluginModel dynamically:

public static string GetCustomizationScript()
{
    return """
           // Set title and header based on the user's name
           model.Title = $"Dynamic Dashboard for {model.User.Name}";
           model.H1 = model.Title;

           // Customize TextBox controls based on role
           model.TextBox1.Label = "Enter your query:";
           model.TextBox1.MaxLength = 100;

           model.TextBox2.Label = "Details:";
           model.TextBox2.MaxLength = 1000;

           // Additional customization if the user is an admin
           if(model.User.IsAdmin)
           {
               model.TextBox1.MaxLength = int.MaxValue;
               model.TextBox2.MaxLength = int.MaxValue;
               // Set an admin-specific footer
               model.Footer = "Admin Dashboard - All rights reserved.";
           }
           else
           {
               model.Footer = "User Dashboard";
           }
           """;
}
Enter fullscreen mode Exit fullscreen mode

The injected script sets up titles, labels, and even a conditional footer, illustrating how you can inject custom logic dynamically into your application.

And here's how you execute the dynamic script using an EvalContext:

var model = new PluginModel
{
    User = new User
    {
        Name = "Anton",
        IsAdmin = false
    }
};

var context = new EvalContext();
// Clear all existing registrations to start fresh
context.UnregisterAll();

// Limit iterations to prevent infinite loops
context.MaxLoopIteration = 5;

// Enable safe mode for restricted code execution
context.SafeMode = true;

// Register default aliases and the necessary types
context.RegisterDefaultAliasSafe();
context.RegisterType(typeof(PluginModel));
context.RegisterType(typeof(User));
context.RegisterType(typeof(TextBox));

context.Execute(GetCustomizationScript(), new { model });

Console.WriteLine(
    JsonSerializer.Serialize(model,
    new JsonSerializerOptions { WriteIndented = true }
));
Enter fullscreen mode Exit fullscreen mode

This produces the following output:

{
  "User": {
    "Name": "Anton",
    "IsAdmin": false
  },
  "Title": "Dynamic Dashboard for Anton",
  "H1": "Dynamic Dashboard for Anton",
  "Footer": "User Dashboard",
  "TextBox1": {
    "Label": "Enter your query:",
    "MaxLength": 100
  },
  "TextBox2": {
    "Label": "Details:",
    "MaxLength": 1000
  }
}
Enter fullscreen mode Exit fullscreen mode

How this works:

  1. A new EvalContext instance is created, and options like MaxLoopIteration and SafeMode are set to secure the evaluation process.

  2. Before executing any dynamic code, register the types (PluginModel, User, and TextBox) so their properties are accessible during execution.

  3. The dynamic code returned by GetCustomizationScript() is executed in the context of the provided model.
    The script customizes the model by setting titles, adjusting text box properties, and conditionally adding a footer based on the user's role.

By using C# Eval Expression in this way, you gain the ability to modify your application's behavior at runtime without redeployment.
This method of code injection, when used responsibly with proper safety measures, opens up exciting possibilities for dynamic application customization.

For further exploration, try a similar example on DotNetFiddle.

A real example where it works in the real project?
You can explore the ZZZ Code AI, which allows you to load custom logic that generates forms, handles business rules, or modifies workflows dynamically.

Screenshot_2

This website uses dynamic code injection to configure multiple forms dynamically through a text file.
The text file contains the code run on the website.

Summary

C# Eval Expression allows you to execute dynamic C# code at runtime, offering innovative approaches to solving runtime challenges.

With it, you can:

  • Execute Dynamic Code: evaluate and run C# code on the fly for flexible application behavior.
  • Create Dynamic Templates: use raw string literals to build personalized email templates that adapt to your data.
  • Construct Dynamic LINQ Queries: build and execute LINQ queries dynamically to filter and sort data with string expressions for IEnumerable and IQueryable.
  • Inject Custom Logic: modify your application's behavior dynamically through safe, customizable code injection using EvalContext.

Start building dynamic apps now with C# Eval Expression.

Disclaimer: this blog post is sponsored by ZZZ Projects.

On my website: antondevtips.com I share .NET and Architecture best practices.
Subscribe to advance your career in .NET.
Download the source code for this blog post for free.

Top comments (0)