【问题标题】:How to deal with graphql listgraphtypes that represent a sql relation如何处理表示 sql 关系的 graphql listgraphtypes
【发布时间】:2020-01-26 14:28:49
【问题描述】:

我面临的问题不是很典型,因为我正在尝试以某种方式制作通用的 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 的作用就像一个访问 orm。

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

【问题讨论】:

  • 说得更清楚些,你需要贴一些代码或者你得到的意想不到的结果。

标签: c# graphql


【解决方案1】:

我使用过 GraphQL,但那是很久以前的事了,
我会给你一个具有一对多关系的样本(如果我没有弄错你的问题)
我有 2 个模型(数据库模型)

public class ProductType
{
    public int Id {get;set;}
    public string Name { get; set; } = "";
    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public int ProductTypeId { get; set; }
    public virtual ProductType ProductType { get; set; }
}

它们将是 2 种类型的 GraphQL

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 ProductTypeObject: ObjectGraphType<ProductType>
{
    private readonly IProductTypeService productType;
    public ProductTypeObject(IProductTypeService pT)
    {
        productType = pT;
        Field(f => f.Name);
        FieldAsync<ListGraphType<ProductObject>>("products",
            resolve: async context => await productType.GetProductsOfType(context.Source.Id));
    }
}

解释一下: IProductTypeServiceIProductService 用于获取数据 productType.GetProductsOfType(context.Source.Id) 将返回 ICollection await product.GetProductType(context.Source.Id) 将返回 ProductType

所以你想查询 1 产品类型有很多产品:

query getProductTypes {
   productTypes{
        id,
        name,
        products {
             id,
             name
        }        
   }
}

希望对你有帮助

【讨论】:

  • 感谢您的输入,但我没有将模型直接映射到 GraphType,我使用 foreach 循环遍历包含所有表名和列名的集合,并且基本上在根查询我正在即时构建它们。我这样做是因为我有大量的模型,而且 1 到 1 的映射并不容易。
  • 哦,我以前试过这个,但还没有找到解决方案。所以我希望有人能解决这个问题。标记。
  • 我现在已经扩展了我的问题,也许现在你可以理解我试图解决的问题了。
【解决方案2】:

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

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

一个Character是ObjectGraphType,但出现In是一个ListGraphType(根据Type也可以是ObjectGraphType)类型Episode 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);

这是至关重要的部分。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-19
    • 2018-02-17
    • 2015-04-12
    • 2011-11-29
    • 1970-01-01
    相关资源
    最近更新 更多