我们有一个大小合适的数据集,我们正在使用 graphql dotnet 进行查询。我们有自定义逻辑来解析 GQL 查询字符串并生成一个函数,该函数将从数据库中选择适当的字段,而不是完整的对象。
因此,我们模式中的“查询”对象有大量几乎相同的调用Field
,只需通过对象的 ID 查找对象,发出解析的选择命令,然后返回结果对象。
例如:
// expression based department query
Field<DepartmentType>(
"department",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "organisationId" },
new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "employerId" },
new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "Id" }),
resolve: context =>
{
var query = context.Document.OriginalQuery.Substring(context.Document.OriginalQuery.IndexOf("{"));
var organisationId = new Guid(context.GetArgument<string>("organisationId"));
var employerId = new Guid(context.GetArgument<string>("employerId"));
var Id = new Guid(context.GetArgument<string>("Id"));
appContext.OrganisationId = organisationId;
appContext.EmployerId = employerId;
var retVal = database.Departments()
.Where(x => x.Id == Id && x.Deleted != true);
var selectStatement = GraphDocumentParser.GenerateSelect(query, "department");
var builder = new ExpressionBuilder();
var statement = builder.BuildSelector<Department, Department>(selectStatement);
var result = retVal.Select(statement);
return result.FirstOrDefault();
}).AuthorizeWith(Constants.GraphQL.Policies.API_ACCESS);
所以我想我可以像这样使用反射来循环我们的graphql类型:
var queryableObjectTypes = Assembly.GetExecutingAssembly().GetTypes()
.Where(x => x.IsClass && x.Namespace == "my.namespace.query.api" && !x.FullName.Contains("+<>c"))
.ToList();
//Loop through our GQL types
foreach (var queryableObjectType in queryableObjectTypes)
{
Console.WriteLine(queryableObjectType.FullName);
//Grab the fields property from the type
var fieldsProperty = queryableObjectType.GetProperties().FirstOrDefault(x => x.Name == "Fields");
//If this has our 'fields' property which we require
if (fieldsProperty != null)
{
//Get the instance of this
var instanceOfType = database.GetServiceProvider().GetService(queryableObjectType);
//If this fields is the type we definitely want - i.e.actually get the value from the
// instance and check it
if (fieldsProperty.GetValue(instanceOfType) is GraphQL.Types.TypeFields typeFields)
{
//If this type contains an employerid, then we need to generate a query for this
if (typeFields.Any(x => x.Name.ToLower() == "employerid"))
{ ... } //These are the objects that I want to generate fields for
这确实为我提供了正确的 GQL 类型对象集合,我可以循环并使用它来添加一个字段(我们的任何一个具有雇主 ID 的对象......我稍后会将其列入黑名单,但这很简单要跳过的列表)
然后,我可以解析该类型指向的“基本”实体等各种位,并获取该字段所需的大部分数据以添加到我们的 GQL 查询中。
我几乎可以获得创建字段类型以添加到我们的查询所需的一切......如下所示:
var f = new FieldType();
f.Name = genericTypeNameFormatted;
f.Description = string.Empty;
f.Type = queryableObjectType;
f.Resolver = ????????
f.Arguments = new QueryArguments(
new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "organisationId" },
new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "employerId" },
new QueryArgument<NonNullGraphType<IdGraphType>> { Name = "Id" });
f.AuthorizeWith(Constants.GraphQL.Policies.API_ACCESS);
但是,我完全坚持使用创建解析器所需的泛型等。它的定义如下:
public class AsyncFieldResolver<TSourceType, TReturnType> : IFieldResolver<Task<TReturnType>>
{
private readonly Func<IResolveFieldContext<TSourceType>, Task<TReturnType>> _resolver;
/// <inheritdoc cref="AsyncFieldResolver{TReturnType}.AsyncFieldResolver(Func{IResolveFieldContext, Task{TReturnType}})"/>
public AsyncFieldResolver(Func<IResolveFieldContext<TSourceType>, Task<TReturnType>> resolver)
{
_resolver = resolver ?? throw new ArgumentNullException(nameof(resolver), "A resolver function must be specified");
}
/// <inheritdoc cref="AsyncFieldResolver{TReturnType}.Resolve(IResolveFieldContext)"/>
public Task<TReturnType> Resolve(IResolveFieldContext context) => _resolver(context.As<TSourceType>());
object IFieldResolver.Resolve(IResolveFieldContext context) => Resolve(context);
}
Func<IResolveFieldContext<TSource>, Task<TReturn>>
是否可以从反射等创建使用数据?还是实际上不可能?