0

我们有一个大小合适的数据集,我们正在使用 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>>是否可以从反射等创建使用数据?还是实际上不可能?

4

0 回答 0