gpt4 book ai didi

c# - 如何使用 Hot Chocolate 和 EF Core 创建 GraphQL 部分更新

转载 作者:行者123 更新时间:2023-12-04 04:14:17 24 4
gpt4 key购买 nike

我正在尝试使用 Entity Framework Core 和 Hot Chocolate 创建一个 ASP.NET Core 3.1 应用程序。
应用程序需要支持通过 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 进行更新,我会遇到 2 个问题。
  • 更新必须包括所有“必填”字段。
  • 此更新尝试将所有未提供的值设置为其默认值。

  • 避免这个问题我看过 Optional<> 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;
    }

    虽然这有效,但它确实有几个主要缺点。
  • 因为 Enity Framework 不理解 Optional<>泛型类型,每个模型都需要 2 个类
  • Update 方法需要有 的条件代码每要更新的字段
    这显然不理想。

  • Entity Framework 可以与 JsonPatchDocument<> 一起使用泛型类。这允许将部分更新应用于实体而无需自定义代码。
    但是,我正在努力寻找一种将其与 Hot Chocolate GraphQL 实现相结合的方法。

    为了完成这项工作,我正在尝试创建一个自定义 InputObjectType,其行为就像使用 Optional<> 定义属性一样。并映射到 JsonPatchDocument<> 的 CLR 类型.这将通过在反射的帮助下为模型类中的每个属性创建自定义映射来工作。然而,我发现一些定义框架处理请求方式的属性 ( IsOptional) 是 Hot Chocolate 框架的内部属性,无法从自定义类中的可覆盖方法访问。

    我也考虑过方法
  • 映射 Optional<>将 UpdateClass 的属性转换为 JsonPatchDocument<>对象
  • 使用代码编织生成具有 Optional<> 的类每个属性的版本
  • 首先覆盖 EF 代码以处理 Optional<>房产

  • 我正在寻找关于如何使用通用方法来实现这一点的任何想法,并避免需要为每种类型编写 3 个单独的代码块——这些代码块需要彼此保持同步。

    最佳答案

    我在使用 Hot Chocolate 时遇到了同样的问题,并且将巨大的表(其中一个有 129 列)映射到对象。为每个表的每个可选属性编写 if 检查会非常痛苦,因此,在下面编写了一个通用的辅助方法以使其更容易:

    /// <summary>
    /// Checks which of the optional properties were passed and only sets those on the db Entity. Also, handles the case where explicit null
    /// value was passed in an optional/normal property and such property would be set to the default value of the property's type on the db entity
    /// Recommendation: Validate the dbEntityObject afterwards before saving to db
    /// </summary>
    /// <param name="inputTypeObject">The input object received in the mutation which has Optional properties as well as normal properties</param>
    /// <param name="dbEntityObject">The database entity object to update</param>
    public void PartialUpdateDbEntityFromGraphQLInputType(object inputTypeObject, object dbEntityObject)
    {
    var inputObjectProperties = inputTypeObject.GetType().GetProperties();
    var dbEntityPropertiesMap = dbEntityObject.GetType().GetProperties().ToDictionary(x => x.Name);
    foreach (var inputObjectProperty in inputObjectProperties)
    {
    //For Optional Properties
    if (inputObjectProperty.PropertyType.Name == "Optional`1")
    {
    dynamic hasValue = inputObjectProperty.PropertyType.GetProperty("HasValue").GetValue(inputObjectProperty.GetValue(inputTypeObject));
    if (hasValue == true)
    {
    var value = inputObjectProperty.PropertyType.GetProperty("Value").GetValue(inputObjectProperty.GetValue(inputTypeObject));
    //If the field was passed as null deliberately to set null in the column, setting it to the default value of the db type in this case.
    if (value == null)
    {
    dbEntityPropertiesMap[inputObjectProperty.Name].SetValue(dbEntityObject, default);
    }
    else
    {
    dbEntityPropertiesMap[inputObjectProperty.Name].SetValue(dbEntityObject, value);
    }
    }
    }
    //For normal required Properties
    else
    {
    var value = inputObjectProperty.GetValue(inputTypeObject);
    //If the field was passed as null deliberately to set null in the column, setting it to the default value of the db type in this case.
    if (value == null)
    {
    dbEntityPropertiesMap[inputObjectProperty.Name].SetValue(dbEntityObject, default);
    }
    else
    {
    dbEntityPropertiesMap[inputObjectProperty.Name].SetValue(dbEntityObject, value);
    }
    }
    }
    }

    然后,在您的示例中,只需像下面这样调用它,并将其重用于所有其他实体更新突变:
    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");

    PartialUpdateDbEntityFromGraphQLInputType(input, item);

    await _context.SaveChangesAsync();

    return item;
    }

    希望这可以帮助。如果有,请将其标记为答案。

    关于c# - 如何使用 Hot Chocolate 和 EF Core 创建 GraphQL 部分更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61064861/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com