Пустые поля при частичной мутации обновления в GraphQL .NET - PullRequest
0 голосов
/ 21 декабря 2018

На работе мы используем EFCore на нашем уровне данных и graphql-dotnet для управления запросами API, у меня проблема с обновлением некоторых наших больших объектов с использованием мутаций GraphQL.Когда пользователь отправляет частичное обновление модели, мы хотели бы обновить в нашей базе данных только те поля, которые фактически были изменены мутацией.Проблема, с которой мы сталкиваемся, заключается в том, что, поскольку мы напрямую сопоставляем входные данные с сущностью, когда какое-то поле было преднамеренно передано как нулевое, или поле вообще не было указано в мутации, мы получаем значение свойства как нулевое.Таким образом, мы не сможем отправить изменения в базу данных, в противном случае мы неверно обновим группу полей до нуля.

Итак, нам нужен способ определить, какие поля отправляются в мутации, и только обновлять их.В JS это достигается путем проверки, является ли значение свойства неопределенным, если значение равно null, мы знаем, что оно было преднамеренно передано как null.

Некоторые обходные пути, о которых мы думали, использовали отражение в словаре дляобновить только указанные поля.Но нам нужно было бы распространить отражение на каждую мутацию.Другое решение состояло в том, чтобы иметь свойство isChanged для каждого обнуляемого свойства в нашей модели и изменить их в указанном установщике свойств, но ... 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 Ответ

0 голосов
/ 24 декабря 2018

Вы можете использовать JsonConvert.PopulateObject из библиотеки Newtonsoft Json.В распознавателе мутаций вместо использования 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);
        });
}
...