在工作中,我们在数据层和graphql-dotnet上使用EFCore来管理API请求,但在使用GraphQL突变更新某些大对象时遇到了问题。当用户发送模型的部分更新时,我们只想在数据库中更新实际上由于突变而更改的字段。我们遇到的问题是,当我们直接将输入映射到实体时,有意将某些字段作为null传递,或者根本没有在突变中指定该字段,我们将属性值设为null。这样,我们就无法将更改发送到数据库,否则我们会错误地将一堆字段更新为null。
因此,我们需要一种方法来识别在突变中发送哪些字段并仅对其进行更新。在JS中,这是通过检查属性值是否未定义来实现的,如果该值为null,我们知道它是有意传递为null的。
我们一直在考虑的一些变通办法是在Dictionary上使用反射来仅更新指定的字段。但是我们需要将反射传播到每个单个突变。另一个解决方案是对模型上的每个可为空的属性都具有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);
});
}
}
答案 0 :(得分:2)
您可以使用Newtonsoft Json库中的JsonConvert.PopulateObject
。在变异解析器上,而不是在我的类型上使用GetArgument
,而是使用GetArgument<dynamic>
并使用JsonConvert.SerializeObject
对其进行序列化,然后调用JsonConvert.PopulateObject
,我只能更新被告知的字段。
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);
});
}