首页 > 解决方案 > 如何处理表示 sql 关系的 graphql listgraphtypes

问题描述

我面临的问题不是很典型,因为我试图以某种方式制作通用的 graphql web api。我根据我在互联网上找到的一些文章解决了我的问题,这些文章我根据自己的需要进行了重构。问题是我正在基于数据库元数据制作一个 graphql api,我已经设法创建了一个根查询,尽管现在的问题在于类型。工作流本质上是循环遍历包含表名和列的集合,将它们添加到类型/字段。然而问题是,当我想说查询子列表时,我没有来自其他类型的子字段。

在本质上:

query getCategoriesAndRelatedProducts {
 categories {
    categoryId
    productList {
      //productList got no fields
    }
  }
}

我没有像这样从模型到 GraphType 进行法线映射

public class ProductObject : ObjectGraphType<Product>
{
    private readonly IProductService product;

    public ProductObject(IProductService p)
    {
        product = p;
        Field(f => f.Id);
        Field(f => f.Name);
        FieldAsync<ProductTypeObject>("type",
            resolve: async context => await product.GetProductType(context.Source.Id));
    }
}

我的根查询是以这种方式完成的

public class NorthWindQuery : ObjectGraphType<object>
    {
        private readonly NorthWindContext _dbContext;
        private IDatabaseMetadata _dbMetadata;
        private ITableNameLookup _tableNameLookup;

        public NorthWindQuery(NorthWindContext dbContext, IDatabaseMetadata dbMetadata,
            ITableNameLookup tableNameLookup)
        {

            _dbMetadata = dbMetadata;
            _tableNameLookup = tableNameLookup;
            _dbContext = dbContext;


            foreach (var metaTable in _dbMetadata.GetTableMetadatas())
            {
                var tableType = new TableType(metaTable);
                var friendlyTableName = _tableNameLookup.GetFriendlyName(metaTable.TableName);

                AddField(new FieldType
                {
                    Name = friendlyTableName,
                    Type = tableType.GetType(),
                    ResolvedType = tableType,
                    Resolver = new MyFieldResolver(metaTable, _dbContext),
                    Arguments = new QueryArguments(
                        tableType.TableArgs
                    )
                });

                // lets add key to get list of current table
                var listType = new ListGraphType(tableType);
                AddField(new FieldType
                {
                    Name = $"{friendlyTableName}_list",
                    Type = listType.GetType(),
                    ResolvedType = listType,
                    Resolver = new MyFieldResolver(metaTable, _dbContext),
                    Arguments = new QueryArguments(
                        tableType.TableArgs
                    )
                });
            }

TableType 是这样完成的:

public class TableType : ObjectGraphType<object>
    {
        public TableType (TableMetadata tableMetadata)
        {
            Name = tableMetadata.TableName;
            foreach (var tableColumn in tableMetadata.Columns)
            {
                InitGraphTableColumn(tableColumn);
            }
            TableArgs.Add(new QueryArgument<IdGraphType> { Name = "id" });
            TableArgs.Add(new QueryArgument<IntGraphType> { Name = "first" });
            TableArgs.Add(new QueryArgument<IntGraphType> { Name = "offset" });
            TableArgs.Add(new QueryArgument<StringGraphType> { Name = "includes" });
        }
        public QueryArguments TableArgs
        {
            get; set;
        }

        private IDictionary<string, Type> _databaseTypeToSystemType;
        protected IDictionary<string, Type> DatabaseTypeToSystemType
        {
            get
            {
                if (_databaseTypeToSystemType == null)
                {
                    _databaseTypeToSystemType = new Dictionary<string, Type>
                    {
                         { "uniqueidentifier", typeof(String) },
                        { "char", typeof(String) },
                        { "nvarchar", typeof(String) },
                        { "int", typeof(int) },
                        { "decimal", typeof(decimal) },
                        { "bit", typeof(bool) }
                    };
                }
                return _databaseTypeToSystemType;
            }
        }

        private void InitGraphTableColumn(ColumnMetadata columnMetadata)
        {
            var graphQLType = ResolveColumnMetaType(columnMetadata.DataType).GetGraphTypeFromType(true);
            var columnField = Field(
                graphQLType,
                columnMetadata.ColumnName
            );

            columnField.Resolver = new NameFieldResolver();
            FillArgs(columnMetadata.ColumnName);
        }

        private void FillArgs(string columnName)
        {
            if(TableArgs == null)
            {
                TableArgs = new QueryArguments(
                    new QueryArgument<StringGraphType>()
                    {
                        Name = columnName
                    });
            }
            else
            {
                TableArgs.Add(new QueryArgument<StringGraphType> { Name = columnName });
            }
        }

        private Type ResolveColumnMetaType(string dbType)
        {
            if (DatabaseTypeToSystemType.ContainsKey(dbType))
                return DatabaseTypeToSystemType[dbType];

            return typeof(String);
        }

基于我只有 1 个 tableType 和 2 个解析器,tableType 是构造类型的类,a 和 2 个解析器用于将 graphql 转换为查询并返回 NameFieldResolver 的值,并且 MyFieldResolver 就像访问形式。

如果有人做过类似的事情,欢迎提供任何帮助。我这样做是因为我有大量模型,并且从模型编写直接映射不是一种选择,因为我必须编写超过 10,000 个方法来运行 api。

标签: c#graphql

解决方案


因此,经过一段时间的思考和调试,我想到我错过了我的方法中的关键一步。我没有将关系视为 GraphType,所以我所做的一切基本上都没有用。所以理论上让我们想象一下最简单的情况:

type Character {
  name: String!
  appearsIn: [Episode]!
}

Character 是 ObjectGraphType,但 appearsIn 是 Type Episode ObjectGraphType 的 ListGraphType(根据类型也可以是 ObjectGraphType)。因此,appearIn 具有所有在 Episode Type 中也可以找到的字段。

这把我带到了这里,我会做一些伪假设,如果有人读到这篇文章,就可以在 TableType 中了解下一步该做什么:

public class TableType : ObjectGraphType<object>
    {
        public TableType (TableMetadata tableMetadata)
        {
            Name = tableMetadata.TableName;
            foreach (var tableColumn in tableMetadata.Columns)
            {
                InitGraphTableColumn(tableColumn);
            }
            TableArgs.Add(new QueryArgument<IdGraphType> { Name = "id" });
            TableArgs.Add(new QueryArgument<IntGraphType> { Name = "first" });
            TableArgs.Add(new QueryArgument<IntGraphType> { Name = "offset" });
            TableArgs.Add(new QueryArgument<StringGraphType> { Name = "includes" });
        }
        public QueryArguments TableArgs
        {
            get; set;
        }

        private IDictionary<string, Type> _databaseTypeToSystemType;
        protected IDictionary<string, Type> DatabaseTypeToSystemType
        {
            get
            {
                if (_databaseTypeToSystemType == null)
                {
                    _databaseTypeToSystemType = new Dictionary<string, Type>
                    {
                         { "uniqueidentifier", typeof(String) },
                        { "char", typeof(String) },
                        { "nvarchar", typeof(String) },
                        { "int", typeof(int) },
                        { "decimal", typeof(decimal) },
                        { "bit", typeof(bool) }
                    };
                }
                return _databaseTypeToSystemType;
            }
        }

        private void InitGraphTableColumn(ColumnMetadata columnMetadata)
        {

        //here check with a if statement is the column.field a object
        // if it is a object make a new fieldtype based on that object
        // pass a resolver to the field
            var graphQLType = ResolveColumnMetaType(columnMetadata.DataType).GetGraphTypeFromType(true);
            var columnField = Field(
                graphQLType,
                columnMetadata.ColumnName
            );

            columnField.Resolver = new NameFieldResolver();
            FillArgs(columnMetadata.ColumnName);
        }

        private void FillArgs(string columnName)
        {
            if(TableArgs == null)
            {
                TableArgs = new QueryArguments(
                    new QueryArgument<StringGraphType>()
                    {
                        Name = columnName
                    });
            }
            else
            {
                TableArgs.Add(new QueryArgument<StringGraphType> { Name = columnName });
            }
        }

        private Type ResolveColumnMetaType(string dbType)
        {
            if (DatabaseTypeToSystemType.ContainsKey(dbType))
                return DatabaseTypeToSystemType[dbType];

            return typeof(String);
        }

所以本质上一个图可以有一个名称、字段和一个解析器,但是字段可以是另一种类型,在创建一个类型时,我们可以简单地调用一个来自另一种类型的字段的创建,我们有我们的关系是来自我们需要的类型的子关系,并且可以访问该类型的字段。

 private void InitGraphTableColumn(ColumnMetadata columnMetadata)
        {

        //here check with a if statement is the column.field a object
        // if it is a object make a new fieldtype based on that object
        // pass a resolver to the field
            var graphQLType = ResolveColumnMetaType(columnMetadata.DataType).GetGraphTypeFromType(true);

这是至关重要的部分。


推荐阅读