Skip to content

Instantly share code, notes, and snippets.

@safakgur
Last active March 9, 2024 19:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save safakgur/10b8f8c4b3cf944968741b532b5e3c45 to your computer and use it in GitHub Desktop.
Save safakgur/10b8f8c4b3cf944968741b532b5e3c45 to your computer and use it in GitHub Desktop.
A URI extension to set query strings using anonymous objects.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Web;
public static class UriHelpers
{
/// <summary>Returns a new URI with a query set to the specified values.</summary>
/// <param name="uri">The URI.</param>
/// <param name="values">
/// The query values, which can be one of:
/// <list type="bullet">
/// <item>
/// <see cref="IEnumerable{T}" /> of
/// <see cref="string" />/<see cref="string" /> or
/// <see cref="string" />/<see cref="object" />.
/// </item>
/// <item><see cref="NameValueCollection" /></item>
/// <item><see cref="IDictionary" /></item>
/// <item><see cref="object" />, whose property names and values will be used.</item>
/// </list>
/// </param>
/// <returns>A new URI with the specified query values.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="uri" /> is null.
/// </exception>
/// <remarks>
/// This method removes the existing query of the URI and adds the specified values ordered
/// by key - performing a stable sort, that is, if two keys are identical, the original
/// order of values are preserved.
/// </remarks>
public static Uri WithQuery(this Uri uri, object? values)
{
ArgumentNullException.ThrowIfNull(uri);
if (values is null)
return uri;
var query = string.Join(
"&", from p in GetQueryValues()
where !string.IsNullOrWhiteSpace(p.Key)
let k = HttpUtility.UrlEncode(p.Key.Trim())
let v = HttpUtility.UrlEncode(p.Value)
orderby k
select string.IsNullOrEmpty(v) ? k : $"{k}={v}");
if (query.Length != 0 || uri.Query.Length != 0)
uri = new UriBuilder(uri) { Query = query }.Uri;
return uri;
IEnumerable<KeyValuePair<string, string>> GetQueryValues()
{
if (values is NameValueCollection nvc)
return from key in nvc.AllKeys
from val in nvc.GetValues(key) ?? []
select KeyValuePair.Create(key, val);
if (values is IEnumerable<KeyValuePair<string, string>> ssd)
return ssd;
if (values is not IEnumerable<KeyValuePair<string, object>> sod)
sod = values is IDictionary ngd
? ngd.Cast<dynamic>().ToDictionary<dynamic, string, object>(
p => ToString(p.Key),
p => p.Value)
: from p in values.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
let v = p.GetValue(values)
select KeyValuePair.Create(p.Name, v);
return from pair in sod
from val in pair.Value as IEnumerable<string?> ?? [ToString(pair.Value)]
select KeyValuePair.Create(pair.Key, val);
}
static string ToString(object value) =>
string.Create(CultureInfo.InvariantCulture, $"{value}");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment