我正在尝试使用 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 方法需要对每个要更新的字段都有条件代码,这显然是不理想的。
实体框架可以与JsonPatchDocument<>
泛型类一起使用。这允许将部分更新应用于实体而无需自定义代码。但是,我正在努力寻找一种将其与 Hot Chocolate GraphQL 实现相结合的方法。
为了完成这项工作,我正在尝试创建一个自定义 InputObjectType,它的行为就像使用定义的属性Optional<>
并映射到JsonPatchDocument<>
. 这将通过在反射的帮助下为模型类中的每个属性创建自定义映射来工作。然而,我发现IsOptional
定义框架处理请求方式的某些属性 () 是 Hot Chocolate 框架的内部属性,无法从自定义类中的可覆盖方法访问。
我也考虑过方法
Optional<>
将 UpdateClass的属性映射到JsonPatchDocument<>
对象中- 使用代码编织生成具有
Optional<>
每个属性版本的类 - 首先覆盖 EF 代码以处理
Optional<>
属性
我正在寻找有关如何使用通用方法来实现这一点的任何想法,并避免需要为每种类型编写 3 个单独的代码块——这些代码块需要彼此保持同步。