Search code examples
c#restdotnet-httpclient

Dynamically constructing a GET query with optional parameters in C#


I have a class that contains all the properties of a query I'm constructing could possibly have (most of which are optional)

For example:

public class QueryClass
{
    public string Offset {get; set;}
    public string Limit {get; set;}
    public string Sort {get; set;}
}

Then in a BuildQuery() method I am constructing the query by doing this:

private string BuildQuery(QueryClass query)
{
    var queryDictionary = new Dictionary<string, string>();
    
    if (!string.IsNullOrEmpty(query.Offset)
    {
        queryDictionary.Add("offset", query.Offset);
    }
    if (!string.IsNullOrEmpty(query.Limit)
    {
        queryDictionary.Add("limit", query.Limit);
    }
    if (!string.IsNullOrEmpty(query.Sort)
    {
        queryDictionary.Add("sort", query.Sort);
    }

    var content = new FormUrlEncodedContent(queryDictionary);

    return content.ReadAsStringAsync().Result;
}

This works, but the issue is my actual QueryClass is significantly larger than this, there's got to be a better way to do this than to have a ton of IF statements for every optional property but I haven't been able to come up with a more elegant solution. I also don't care for adding the keys in the dictionary in this way, I probably need a new approach for how I structure the QueryClass.


Solution

  • If you don't mind taking the reflection hit, just project, filter nulls, then send to ToDictionary

    Note : The assumptions here are, all the property names are your keys, and all the properties are convertible to string

    var queryClass = new QueryClass()
    {
        Limit = "asdf",
        Sort = "Bob"
    };
    
    var results = queryClass
        .GetType()
        .GetProperties()
        .Select(x => (Value: x.GetValue(queryClass) as string, x.Name))
        .Where(x => !string.IsNullOrWhiteSpace(x.Value))
        .ToDictionary(
            x => x.Name.ToLower(), 
            x => x.Value);
    
    foreach (var (key, value) in results)
        Console.WriteLine($"{key} : {value}");
    

    Output

    limit : asdf
    sort : Bob
    

    Add pepper and salt to taste

    Or as an extension method

    public static IDictionary<string, string> ConvertToDictionary<T>(this T source)
        => typeof(T).GetProperties()
            .Select(x => (Value: x.GetValue(source) as string, x.Name))
            .Where(x => !string.IsNullOrWhiteSpace(x.Value))
            .ToDictionary(
                x => x.Name.ToLower(),
                x => x.Value!);