Skip to content

Commit 4b047d2

Browse files
Improve exception handling, enum numeric value
1 parent be976a1 commit 4b047d2

File tree

2 files changed

+54
-40
lines changed

2 files changed

+54
-40
lines changed

QueryExecutor.cs

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Collections.Generic;
1+
using System.ComponentModel.DataAnnotations;
22
using System.Globalization;
33
using System.Linq.Expressions;
44
using System.Reflection;
@@ -34,13 +34,15 @@ public static object[] Run<TSource, TTarget>(this IQueryable<TSource> source, Qu
3434
return db is not null ? BuildQuery<TSource, TTarget>(db, source, query, mapper).ToArray() : Array.Empty<object>();
3535
}
3636

37-
public static MethodInfo? GetGenericMethod(Type executorType, int attributeCount, Type[] genericArgs)
37+
public static MethodInfo? GetRunMethod(Type executorType, Type[] genericArgs)
3838
{
39-
var method = executorType
39+
return executorType
4040
.GetMethods(BindingFlags.Static | BindingFlags.Public)
41-
.FirstOrDefault(m => m.CustomAttributes.Count() == attributeCount);
42-
43-
return method?.MakeGenericMethod(genericArgs);
41+
.FirstOrDefault(m =>
42+
m.Name == "Run" &&
43+
m.IsGenericMethodDefinition &&
44+
m.GetGenericArguments().Length == genericArgs.Length)
45+
?.MakeGenericMethod(genericArgs);
4446
}
4547

4648
private static IQueryable<object> BuildQuery<TSource, TTarget>(DbContext db, IQueryable<TSource> source, Query? query, IMapper? mapper = null)
@@ -232,23 +234,19 @@ private static Expression BuildInExpression<T>(DbContext db, Query? query, Membe
232234

233235
private static IEnumerable<dynamic> RunSubquery(DbContext db, Query? query)
234236
{
235-
var t = query?.Entity.ToLower(CultureInfo.InvariantCulture) ?? string.Empty;
236-
var p = db.GetType().GetProperty(t, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) ?? throw new InvalidOperationException($"Property '{t}' not found on type '{db.GetType()}'");
237+
var propName = query?.Entity.ToLower(CultureInfo.InvariantCulture) ?? string.Empty;
238+
var prop = db.GetType().GetProperty(propName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)
239+
?? throw new InvalidOperationException($"Property '{propName}' not found on type '{db.GetType()}'");
237240

238241
var methods = typeof(QueryExecutor).GetMethods(BindingFlags.Static | BindingFlags.Public);
239242
var method = methods?.FirstOrDefault(m => m.CustomAttributes.Count() == 1);
240-
var dbSet = p.GetValue(db);
241-
var genericType = p.PropertyType.GetGenericArguments().FirstOrDefault();
242-
243-
if (dbSet != null && genericType != null)
244-
{
243+
var dbSet = prop.GetValue(db) ?? throw new ValidationException($"DbSet property '{prop.Name}' is null in DbContext.");
244+
var genericType = prop.PropertyType.GetGenericArguments().FirstOrDefault() ?? throw new ValidationException($"Missing DbSet generic type");
245245

246-
var genericMethod = GetGenericMethod(typeof(QueryExecutor), 1, [genericType]);
246+
var genericMethod = GetRunMethod(typeof(QueryExecutor), [genericType]);
247247

248-
var queryable = dbSet?.GetType().GetMethod("AsQueryable")?.Invoke(dbSet, null);
249-
return genericMethod?.Invoke(null, [queryable, query]) as IEnumerable<dynamic> ?? Array.Empty<dynamic>();
250-
}
251-
return Enumerable.Empty<dynamic>();
248+
var queryable = dbSet?.GetType().GetMethod("AsQueryable")?.Invoke(dbSet, null);
249+
return genericMethod?.Invoke(null, [queryable, query]) as IEnumerable<dynamic> ?? Array.Empty<dynamic>();
252250
}
253251

254252
private static dynamic? ProjectField(object? obj, string field)
@@ -269,10 +267,21 @@ private static Expression GetSearchValue(JsonValue? jsonVal, Type targetType)
269267

270268
if (nonNullableType.IsEnum)
271269
{
272-
var enumValue = jsonVal.Deserialize<string>();
273-
if (enumValue != null)
270+
if (valueKind == JsonValueKind.String)
271+
{
272+
var enumValue = jsonVal.Deserialize<string>();
273+
if (enumValue != null)
274+
{
275+
return Expression.Constant(Enum.Parse(nonNullableType, enumValue));
276+
}
277+
}
278+
else if (valueKind == JsonValueKind.Number)
274279
{
275-
return Expression.Constant(Enum.Parse(nonNullableType, enumValue));
280+
var enumValue = jsonVal.Deserialize<int?>();
281+
if (enumValue != null)
282+
{
283+
return Expression.Constant(Enum.ToObject(nonNullableType, enumValue));
284+
}
276285
}
277286
}
278287

Services/QueryBuilderService.cs

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
using AutoMapper;
2-
using Microsoft.EntityFrameworkCore;
3-
using Microsoft.Extensions.Logging;
1+
using System.ComponentModel.DataAnnotations;
42
using System.Globalization;
53
using System.Reflection;
4+
using AutoMapper;
5+
using Microsoft.EntityFrameworkCore;
6+
using Microsoft.Extensions.Logging;
67

78
namespace Infragistics.QueryBuilder.Executor
89
{
@@ -15,23 +16,27 @@ public Dictionary<string, object[]> RunQuery(Query query)
1516
var t = query.Entity.ToLower(CultureInfo.InvariantCulture);
1617

1718
var propInfo = db?.GetType().GetProperties()
18-
.FirstOrDefault(p => p.PropertyType.IsGenericType && p.Name.ToLower(CultureInfo.InvariantCulture) == t && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>));
19-
if (propInfo != null)
19+
.FirstOrDefault(p =>
20+
p.PropertyType.IsGenericType &&
21+
p.Name.ToLower(CultureInfo.InvariantCulture) == t &&
22+
p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
23+
?? throw new InvalidOperationException($"Unknown entity {t}");
24+
25+
var dbSet = propInfo.GetValue(db) ?? throw new ValidationException($"DbSet property '{propInfo.Name}' is null in DbContext.");
26+
var dbGenericType = dbSet.GetType().GenericTypeArguments.FirstOrDefault() ?? throw new ValidationException($"Missing DbSet generic type");
27+
28+
var resultProperty = typeof(TResults).GetProperty(t, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase)
29+
?? throw new ValidationException($"Unknown entity {t}");
30+
31+
var dtoGenericType = resultProperty.PropertyType.GetElementType() ?? throw new ValidationException($"Missing Dto generic type");
32+
33+
var genericMethod = QueryExecutor.GetRunMethod(typeof(QueryExecutor), [dbGenericType, dtoGenericType]);
34+
35+
var queryable = dbSet.GetType().GetMethod("AsQueryable")?.Invoke(dbSet, null)
36+
?? throw new InvalidOperationException($"DbSet '{propInfo.Name}' does not support AsQueryable().");
37+
if ( genericMethod?.Invoke(null, [queryable, query, mapper]) is object[] propRes)
2038
{
21-
var dbSet = propInfo.GetValue(db);
22-
var dbGenericType = dbSet?.GetType()?.GenericTypeArguments.FirstOrDefault();
23-
var dtoGenericType = typeof(TResults).GetProperty(propInfo.Name)?.PropertyType.GetElementType();
24-
25-
if (dbSet != null && dbGenericType != null && dtoGenericType != null)
26-
{
27-
var genericMethod = QueryExecutor.GetGenericMethod(typeof(QueryExecutor), 2, [dbGenericType, dtoGenericType]);
28-
29-
var queryable = dbSet.GetType().GetMethod("AsQueryable")?.Invoke(dbSet, null);
30-
if (queryable != null && genericMethod?.Invoke(null, [queryable, query, mapper]) is object[] propRes)
31-
{
32-
return new Dictionary<string, object[]> { { propInfo.Name.ToLowerInvariant(), propRes } };
33-
}
34-
}
39+
return new Dictionary<string, object[]> { { propInfo.Name.ToLowerInvariant(), propRes } };
3540
}
3641

3742
throw new InvalidOperationException($"Unknown entity {t}");

0 commit comments

Comments
 (0)