Skip to content

Simplify convert strings to list #1108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions src/GraphQL.EntityFramework/Where/ArgumentReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,8 @@ public static bool TryReadWhere(IResolveFieldContext context, out IReadOnlyColle
public static IReadOnlyCollection<OrderBy> ReadOrderBy(IResolveFieldContext context) =>
ReadList<OrderBy>(context, "orderBy");

public static bool TryReadIds(IResolveFieldContext context, [NotNullWhen(true)] out string[]? idValues)
public static bool TryReadIds(IResolveFieldContext context, [NotNullWhen(true)] out object[]? idValues)
{
static string ArgumentToExpression(object argument) =>
argument switch
{
long l => l.ToString(CultureInfo.InvariantCulture),
int i => i.ToString(CultureInfo.InvariantCulture),
string s => s,
_ => throw new($"TryReadId got an 'id' argument of type '{argument.GetType().FullName}' which is not supported.")
};

var arguments = context.Arguments;
if (arguments == null)
{
Expand All @@ -43,7 +34,7 @@ static string ArgumentToExpression(object argument) =>
return false;
}

var expressions = new List<string>();
var expressions = new List<object>();

if (id.Source != ArgumentSource.FieldDefault)
{
Expand All @@ -53,7 +44,7 @@ static string ArgumentToExpression(object argument) =>
throw new("Null 'id' is not supported.");
}

expressions.Add(ArgumentToExpression(idValue));
expressions.Add(idValue);
}

if (ids.Source != ArgumentSource.FieldDefault)
Expand All @@ -63,7 +54,7 @@ static string ArgumentToExpression(object argument) =>
throw new($"TryReadIds got an 'ids' argument of type '{ids.Value!.GetType().FullName}' which is not supported.");
}

expressions.AddRange(objCollection.Select(ArgumentToExpression));
expressions.AddRange(objCollection);
}

idValues = expressions.ToArray();
Expand Down
35 changes: 21 additions & 14 deletions src/GraphQL.EntityFramework/Where/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,18 @@ static Expression MakePredicateBody(IReadOnlyCollection<WhereExpression> wheres)
// Otherwise handle single expressions
else
{
// Get the predicate body for the single expression
nextExpression = MakePredicateBody(where.Path, where.Comparison, where.Value, where.Negate);

if (where.Value != null)
{
var property = PropertyCache<T>.GetProperty(where.Path);
var values = TypeConverter.ConvertStringsToList(where.Value, property.PropertyType);
// Get the predicate body for the single expression
nextExpression = MakePredicateBody(where.Path, where.Comparison, values.ToArray(), where.Negate);
}
else
{
nextExpression = MakePredicateBody(where.Path, where.Comparison, null, where.Negate);
}
}

// If this is the first where processed
Expand All @@ -65,15 +75,15 @@ static Expression MakePredicateBody(IReadOnlyCollection<WhereExpression> wheres)
/// <summary>
/// Create a single predicate for the single set of supplied conditional arguments
/// </summary>
public static Expression<Func<T, bool>> BuildPredicate(string path, Comparison comparison, string?[]? values, bool negate = false)
public static Expression<Func<T, bool>> BuildPredicate(string path, Comparison comparison, object?[]? values, bool negate = false)
{
var expressionBody = MakePredicateBody(path, comparison, values, negate);
var param = PropertyCache<T>.SourceParameter;

return Expression.Lambda<Func<T, bool>>(expressionBody, param);
}

static Expression MakePredicateBody(string path, Comparison comparison, string?[]? values, bool negate)
static Expression MakePredicateBody(string path, Comparison comparison, object?[]? values, bool negate)
{
try
{
Expand Down Expand Up @@ -106,7 +116,7 @@ static Expression MakePredicateBody(string path, Comparison comparison, string?[
}
}

static Expression ProcessList(string path, Comparison comparison, string?[]? values)
static Expression ProcessList(string path, Comparison comparison, object?[]? values)
{
// Get the path pertaining to individual list items
var listPath = ListPropertyRegex().Match(path).Groups[1].Value;
Expand Down Expand Up @@ -152,7 +162,7 @@ static Expression ProcessList(string path, Comparison comparison, string?[]? val
return Expression.Call(anyInfo, property.Left, subPredicate);
}

static Expression GetExpression(string path, Comparison comparison, string?[]? values)
static Expression GetExpression(string path, Comparison comparison, object?[]? values)
{
var property = PropertyCache<T>.GetProperty(path);
Expression expressionBody;
Expand Down Expand Up @@ -193,21 +203,18 @@ static Expression GetExpression(string path, Comparison comparison, string?[]? v
default:
WhereValidator.ValidateSingleObject(property.PropertyType, comparison);
var value = values?.Single();
var valueObject = TypeConverter.ConvertStringToType(value, property.PropertyType);
expressionBody = MakeSingleObjectComparison(comparison, valueObject, property);
expressionBody = MakeSingleObjectComparison(comparison, value, property);
break;
}
}

return expressionBody;
}

static Expression MakeObjectListInComparision(string[] values, Property<T> property)
static Expression MakeObjectListInComparision(object[] values, Property<T> property)
{
// Attempt to convert the string values to the object type
var objects = TypeConverter.ConvertStringsToList(values, property.Info);
// Make the object values a constant expression
var constant = Expression.Constant(objects);
var constant = Expression.Constant(values);
// Build and return the expression body
if (property.ListContains is null)
{
Expand All @@ -217,7 +224,7 @@ static Expression MakeObjectListInComparision(string[] values, Property<T> prope
return Expression.Call(constant, property.ListContains, property.Left);
}

static Expression MakeStringListInComparison(string[] values, Property<T> property)
static Expression MakeStringListInComparison(object[] values, Property<T> property)
{
var equalsBody = Expression.Call(null, ReflectionCache.StringEqual, ExpressionCache.StringParam, property.Left);

Expand All @@ -228,7 +235,7 @@ static Expression MakeStringListInComparison(string[] values, Property<T> proper
return Expression.Call(null, ReflectionCache.StringAny, Expression.Constant(values), itemEvaluate);
}

static Expression MakeSingleStringComparison(Comparison comparison, string? value, Property<T> property)
static Expression MakeSingleStringComparison(Comparison comparison, object? value, Property<T> property)
{
var left = property.Left;

Expand Down
56 changes: 28 additions & 28 deletions src/GraphQL.EntityFramework/Where/TypeConverter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
static class TypeConverter
{
public static IList ConvertStringsToList(string?[] values, MemberInfo property)
public static List<object?> ConvertStringsToList(string?[] values, MemberInfo property)
{
var hash = new HashSet<string?>();
var duplicates = values.Where(_ => !hash.Add(_)).ToArray();
Expand Down Expand Up @@ -37,126 +37,126 @@ static bool ParseBoolean(string value) =>
_ => bool.Parse(value)
};

static IList ConvertStringsToListInternal(IEnumerable<string> values, Type type)
static List<object?> ConvertStringsToListInternal(IEnumerable<string> values, Type type)
{
if (type == typeof(Guid))
{
return values.Select(Guid.Parse).ToList();
return values.Select(s => (object?)Guid.Parse(s)).ToList();
}
if (type == typeof(Guid?))
{
return values.Select(_ => (Guid?)new Guid(_)).ToList();
return values.Select(_ => (object?)new Guid(_)).ToList();
}

if (type == typeof(bool))
{
return values.Select(ParseBoolean).ToList();
return values.Select(s => (object?)ParseBoolean(s)).ToList();
}
if (type == typeof(bool?))
{
return values.Select(_ => (bool?)ParseBoolean(_)).ToList();
return values.Select(_ => (object?)ParseBoolean(_)).ToList();
}

if (type == typeof(int))
{
return values.Select(int.Parse).ToList();
return values.Select(s => (object?)int.Parse(s)).ToList();
}
if (type == typeof(int?))
{
return values.Select(_ => (int?)int.Parse(_)).ToList();
return values.Select(_ => (object?)int.Parse(_)).ToList();
}

if (type == typeof(short))
{
return values.Select(short.Parse).ToList();
return values.Select(s => (object?)short.Parse(s)).ToList();
}
if (type == typeof(short?))
{
return values.Select(_ => (short?)short.Parse(_)).ToList();
return values.Select(_ => (object?)short.Parse(_)).ToList();
}

if (type == typeof(long))
{
return values.Select(long.Parse).ToList();
return values.Select(s => (object?)long.Parse(s)).ToList();
}
if (type == typeof(long?))
{
return values.Select(_ => (long?)long.Parse(_)).ToList();
return values.Select(_ => (object?)long.Parse(_)).ToList();
}

if (type == typeof(uint))
{
return values.Select(uint.Parse).ToList();
return values.Select(s => (object?)uint.Parse(s)).ToList();
}
if (type == typeof(uint?))
{
return values.Select(_ => (uint?)uint.Parse(_)).ToList();
return values.Select(_ => (object?)uint.Parse(_)).ToList();
}

if (type == typeof(ushort))
{
return values.Select(ushort.Parse).ToList();
return values.Select(s =>(object?) ushort.Parse(s)).ToList();
}
if (type == typeof(ushort?))
{
return values.Select(_ => (ushort?)ushort.Parse(_)).ToList();
return values.Select(_ => (object?)ushort.Parse(_)).ToList();
}

if (type == typeof(ulong))
{
return values.Select(ulong.Parse).ToList();
return values.Select(s => (object?)ulong.Parse(s)).ToList();
}
if (type == typeof(ulong?))
{
return values.Select(_ => (ulong?)ulong.Parse(_)).ToList();
return values.Select(_ => (object?)ulong.Parse(_)).ToList();
}

if (type == typeof(DateTime))
{
return values.Select(DateTime.Parse).ToList();
return values.Select(s => (object?)DateTime.Parse(s)).ToList();
}
if (type == typeof(DateTime?))
{
return values.Select(_ => (DateTime?)DateTime.Parse(_)).ToList();
return values.Select(_ => (object?)DateTime.Parse(_)).ToList();
}

if (type == typeof(Time))
{
return values.Select(Time.Parse).ToList();
return values.Select(s => (object?)Time.Parse(s)).ToList();
}
if (type == typeof(Time?))
{
return values.Select(_ => (Time?)Time.Parse(_)).ToList();
return values.Select(_ => (object?)Time.Parse(_)).ToList();
}

if (type == typeof(Date))
{
return values.Select(_ => Date.ParseExact(_, "yyyy-MM-dd")).ToList();
return values.Select(_ => (object?)Date.ParseExact(_, "yyyy-MM-dd")).ToList();
}
if (type == typeof(Date?))
{
return values.Select(_ => (Date?)Date.ParseExact(_, "yyyy-MM-dd")).ToList();
return values.Select(_ => (object?)Date.ParseExact(_, "yyyy-MM-dd")).ToList();
}

if (type == typeof(DateTimeOffset))
{
return values.Select(DateTimeOffset.Parse).ToList();
return values.Select(s => (object?)DateTimeOffset.Parse(s)).ToList();
}
if (type == typeof(DateTimeOffset?))
{
return values.Select(_ => (DateTimeOffset?)DateTimeOffset.Parse(_)).ToList();
return values.Select(_ => (object?)DateTimeOffset.Parse(_)).ToList();
}

if (type.IsEnum)
{
var getList = enumListMethod.MakeGenericMethod(type);
return (IList)getList.Invoke(null, [values])!;
return (List<object?>)getList.Invoke(null, [values])!;
}

if (type.TryGetEnumType(out var enumType))
{
var getList = nullableEnumListMethod.MakeGenericMethod(enumType);
return (IList)getList.Invoke(null, [values])!;
return (List<object?>)getList.Invoke(null, [values])!;
}

throw new($"Could not convert strings to {type.FullName}.");
Expand Down