Search code examples
linqlinq-expressionsdynamic-linqdynamic-linq-core

How to escape string for dynamic linq where clause


I am using Dynamic LINQ to find the records using dynamic where clause. I have instance of Person class

public class Person
{
   public string Name {get;set;}
   public int Age {get;set;}
   public string Address {get;set;}
}

var person = new Person()
{
   Name = "Foo Bar",
   Age = 25,
   Address = "1234 ABC \"XYZ\" Road"
}

Assume that I already have the collection of properties to include in the where clause.

var properties = new string[] {"Name","Age","Address"};

I am constructing the where clause using reflection. Based on the documentation

if the value is string then we need to escape it in the double quotes.

where clause construction

var type = person.GetType();
var clause = "";
foreach (var propertyName in properties)
{
    var p = type.GetProperty(propertyName);
    var val = p.GetValue(person);
    if (val != null)
    {
        if (p.PropertyType == typeof(string))
        {
            val = $"\"{val}\"";
        }

        clause = clause == "" ? $"{propertyName} == {val}" : $"{clause} && {propertyName} == {val}";
    }
}

find person

var found = await dbContext.Persons
             .Where(condition)
             .ToListAsync();

This is failing because it not escaping the Address value correctly. The correct clause should be

"Name == \"Foo Bar\" && Age == 25 && Address= \"1234 ABC \\\"XYZ\\\" Road\""

Is there a library in .NET that can escape string? I tried Regex.Escape() That didn't work


Solution

  • DynamicLinq supports parameters for this purpose. See https://dynamic-linq.net/basic-simple-query#strongly-typed-linq-versus-dynamic-linq

    The tricky thing in your case is keeping track of the parameters and their names as dynamic values. I think something like this should work.

    var type = person.GetType();
    var clauses = new List<string>();
    var parameters = new List<object>();
    foreach (var propertyName in properties)
    {
        var p = type.GetProperty(propertyName);
        var val = p.GetValue(person);
        if (val != null)
        {
            var parameterName = $"@{parameters.Count}";
            parameters.Add(val);
            clauses.Add($"{propertyName} == {parameterName}");
        }
    }
    var condition = string.Join(" && ", clauses);
    ...
    var found = await dbContext.Persons
                 .Where(condition, parameters.ToArray())
                 .ToListAsync();