GraphQL .NET 上部分更新突变的空字段

Posted

技术标签:

【中文标题】GraphQL .NET 上部分更新突变的空字段【英文标题】:Null fields on a partial update mutation on GraphQL .NET 【发布时间】:2019-05-22 00:46:21 【问题描述】:

在工作中,我们在数据层和 graphql-dotnet 上使用 EFCore 来管理 API 请求,我在使用 GraphQL 突变更新我们的一些大对象时遇到了问题。当用户发送模型的部分更新时,我们希望仅在数据库上更新实际被突变更改的字段。我们遇到的问题是,当我们直接将输入映射到实体时,无论某些字段是故意作为 null 传递的,还是根本没有在突变上指定该字段,我们都会将属性值设为 null。这样我们就无法将更改发送到数据库,否则我们会错误地将一堆字段更新为 null。

因此,我们需要一种方法来识别哪些字段在突变中发送并仅更新这些字段。在 JS 中,这是通过检查属性值是否未定义来实现的,如果值为 null,我们知道它是故意作为 null 传递的。

我们一直在考虑的一些解决方法是使用字典上的反射来仅更新指定的字段。但是我们需要将反射传播到每一个突变。另一种解决方案是为模型上的每个可空属性设置一个 isChanged 属性,并在引用的属性设置器上更改 ir,但是... cmon...

我在下面提供了一些代码作为这种情况的示例:

人类阶级:

public class Human

    public Id  get; set; 
    public string Name  get; set; 
    public string HomePlanet  get; set; 

GraphQL 类型:

public class HumanType : ObjectGraphType<Human>

    public HumanType()
    
        Name = "Human";
        Field(h => h.Id).Description("The id of the human.");
        Field(h => h.Name, nullable: true).Description("The name of the human.");
        Field(h => h.HomePlanet, nullable: true).Description("The home planet of the human.");
    

输入类型:

public class HumanInputType : InputObjectGraphType
    
        public HumanInputType()
        
            Name = "HumanInput";
            Field<NonNullGraphType<StringGraphType>>("name");
            //The problematic field
            Field<StringGraphType>("homePlanet");
        
    

人类变异:

/// Example JSON request for an update mutation without HomePlanet 
/// 
///   "query": "mutation ($human:HumanInput!) createHuman(human: $human)  id name  ",
///   "variables": 
///     "human": 
///       "name": "Boba Fett"
///     
///   
/// 
///
public class StarWarsMutation : ObjectGraphType<object>

    public StarWarsMutation(StarWarsRepository data)
    
        Name = "Mutation";

        Field<HumanType>(
            "createOrUpdateHuman",
            arguments: new QueryArguments(
                new QueryArgument<NonNullGraphType<HumanInputType>> Name = "human"
            ),
            resolve: context =>
            
                //After conversion human.HomePlanet is null. But it was not informed, we should keep what is on the database at the moment
                var human = context.GetArgument<Human>("human");
                //On EFCore the Update method is equivalent to an InsertOrUpdate method
                return data.Update(human);
            );
    

【问题讨论】:

【参考方案1】:

您可以使用 Newtonsoft Json 库中的 JsonConvert.PopulateObject。在突变解析器上,我使用GetArgument&lt;dynamic&gt; 并使用JsonConvert.SerializeObject 对其进行序列化,然后调用JsonConvert.PopulateObject,而不是在我的类型中使用GetArgument,我只能更新被告知的字段。

public StarWarsMutation(StarWarsRepository data)

    Name = "Mutation";

    Field<HumanType>(
        "createOrUpdateHuman",
        arguments: new QueryArguments(
            new QueryArgument<NonNullGraphType<HumanInputType>> Name = "human"
        ),
        resolve: context =>
        
            //After conversion human.HomePlanet is null. But it was not informed, we should keep what is on the database at the moment
            var human = context.GetArgument<dynamic>("human");
            var humanDb = data.GetHuman(human["id"]);
            var json = JsonConvert.SerializeObject(human);
            JsonConvert.PopulateObject(json, humanDb);
            //On EFCore the Update method is equivalent to an InsertOrUpdate method
            return data.Update(humanDb);
        );

【讨论】:

以上是关于GraphQL .NET 上部分更新突变的空字段的主要内容,如果未能解决你的问题,请参考以下文章

Sequelize with GraphQl:如何使用突变更新字段

在 Graphcool / GraphQL 中发布更新突变时如何“更新”数组/嵌套字段?

用于在突变字段上映射数组的 GraphQL 语法

如何使用 GraphQL 突变进行更新(热巧克力)

Apollo GraphQL:未在突变子字段上调用解析器

嵌套资源上的 GraphQL 突变