Как я могу создать частичное обновление GraphQL с HotChocolate и EFCore - PullRequest
1 голос
/ 06 апреля 2020

Я пытаюсь создать приложение ASP. NET Core 3.1, используя Entity Framework Core и Hot Chocolate. Приложение должно поддерживать создание, запросы, обновление и удаление объектов через GraphQL. В некоторых полях должны быть значения.

Создание, запрос и удаление объектов не является проблемой, однако обновление объектов более сложно. Проблема, которую я пытаюсь решить, заключается в частичном обновлении.

Следующий объект модели используется Entity Framework для создания таблицы базы данных с помощью кода вначале.

public class Warehouse
{
    [Key]
    public int Id { get; set; }

    [Required]
    public string Code { get; set; }
    public string CompanyName { get; set; }
    [Required]
    public string WarehouseName { get; set; }
    public string Telephone { get; set; }
    public string VATNumber { get; set; }
}

Я могу создать запись в базе данных с мутацией определила что-то вроде этого:

public class WarehouseMutation : ObjectType
{
    protected override void Configure(IObjectTypeDescriptor descriptor)
    {
        descriptor.Field("create")
            .Argument("input", a => a.Type<InputObjectType<Warehouse>>())
            .Type<ObjectType<Warehouse>>()
            .Resolver(async context =>
            {
                var input = context.Argument<Warehouse>("input");
                var provider = context.Service<IWarehouseStore>();

                return await provider.CreateWarehouse(input);
            });
    }
}

На данный момент объекты маленькие, но у них будет гораздо больше полей до завершения проекта. Мне нужно оставить возможность GraphQL отправлять данные только для тех полей, которые изменились, однако, если я использую тот же InputObjectType для обновлений, я сталкиваюсь с двумя проблемами.

  1. Обновление должно включать все "Обязательно" "fields.
  2. Обновление пытается установить для всех непредоставленных значений их значения по умолчанию.

Чтобы избежать этой проблемы, я рассмотрел предоставленный тип Optional<> generi c от HotChocolate. Это требует определения нового типа «Обновление», например:

public class WarehouseUpdate
{
    public int Id { get; set; } // Must always be specified
    public Optional<string> Code { get; set; }
    public Optional<string> CompanyName { get; set; }
    public Optional<string> WarehouseName { get; set; }
    public Optional<string> Telephone { get; set; }
    public Optional<string> VATNumber { get; set; }
}

Добавление этого к мутации

descriptor.Field("update")
            .Argument("input", a => a.Type<InputObjectType<WarehouseUpdate>>())
            .Type<ObjectType<Warehouse>>()
            .Resolver(async context =>
            {
                var input = context.Argument<WarehouseUpdate>("input");
                var provider = context.Service<IWarehouseStore>();

                return await provider.UpdateWarehouse(input);
            });

Затем метод UpdateWarehouse должен обновить только те поля, которые были предоставлены значение.

public async Task<Warehouse> UpdateWarehouse(WarehouseUpdate input)
{
    var item = await _context.Warehouses.FindAsync(input.Id);
    if (item == null)
        throw new KeyNotFoundException("No item exists with specified key");

    if (input.Code.HasValue)
        item.Code = input.Code;
    if (input.WarehouseName.HasValue)
        item.WarehouseName = input.WarehouseName;
    if (input.CompanyName.HasValue)
        item.CompanyName = input.CompanyName;
    if (input.Telephone.HasValue)
        item.Telephone = input.Telephone;
    if (input.VATNumber.HasValue)
        item.VATNumber = input.VATNumber;

    await _context.SaveChangesAsync();

    return item;
}

Хотя это работает, у него есть пара существенных недостатков.

  1. Поскольку Enity Framework не понимает типы Optional<> generi c, каждая модель потребует 2 класса
  2. Метод обновления должен иметь условный код для каждого поля для обновления Это, очевидно, не идеально.

Entity Framework может использоваться вместе с классом JsonPatchDocument<> generi c. Это позволяет применять частичные обновления к объекту, не требуя специального кода. Однако я изо всех сил пытаюсь найти способ объединить это с реализацией Hot Chocolate GraphQL.

Чтобы сделать эту работу, я пытаюсь создать собственный InputObjectType, который ведет себя так, как будто свойства определены с использованием Optional<> и отображается на тип CLR JsonPatchDocument<>. Это будет работать путем создания пользовательских сопоставлений для каждого свойства в классе модели с помощью отражения. Однако я обнаружил, что некоторые из свойств (IsOptional), которые определяют способ обработки запроса средой, являются внутренними по отношению к платформе Hot Chocolate и недоступны из переопределяемых методов в пользовательском классе.

I Также рассмотрены способы

  • Отображение свойств Optional<> UpdateClass в JsonPatchDocument<> объект
  • Использование ткачества кода для генерации класса с Optional<> версиями каждого свойства
  • Переопределение кода EF в первую очередь для обработки Optional<> свойств

Я ищу любые идеи относительно того, как я могу реализовать это, используя обобщенный c подход и избегая необходимости писать 3 отдельных кодовых блока для каждого типа - которые должны быть синхронизированы c друг с другом.

...