ASP.NET Core中使用GraphQL - 目录


到目前为止我们一直在使用GraphQL操作单个实体。在本篇博文中,我们将使用GraphQL操作实体集合。

这里我们使用的场景是处理一个顾客的所有订单,顾客和订单之间的关系是一对多。一个顾客可以有多个订单,相应的一个订单只属于一个顾客。

数据库修改

下面我们首先创建2个新的类CustomerOrder

Customer
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
public string BillingAddress { get; set; }
public IEnumerable<Order> Orders { get; set; }
}
Order
public class Order
{
public int OrderId { get; set; }
public string Tag { get; set; }
public DateTime CreatedAt { get; set; } public Customer Customer { get; set; }
public int CustomerId { get; set; }
}

然后我们修改ApplicationDbContext类,在OnModelCreating配置一下表的主外键。

modelBuilder.Entity<Customer>()
.HasKey(p => p.CustomerId);
modelBuilder.Entity<Customer>().HasMany(p => p.Orders)
.WithOne()
.HasForeignKey(p => p.CustomerId); modelBuilder.Entity<Order>().HasKey(p => p.OrderId);

最后我们使用如下命令创建迁移并更新数据库

dotnet ef migrations add OneToManyRelationship
dotnet ef database update

至此数据库修改完成。

添加GraphQL代码

下面我们需要添加GraphQL针对CustomerOrder表的字段配置。

OrderType
public class OrderType: ObjectGraphType <Order> {
public OrderType(IDataStore dataStore) {
Field(o => o.Tag);
Field(o => o.CreatedAt);
Field <CustomerType, Customer> ()
.Name("Customer")
.ResolveAsync(ctx => {
return dataStore.GetCustomerByIdAsync(ctx.Source.CustomerId);
});
}
}
CustomerType.cs
public class CustomerType: ObjectGraphType <Customer> {
public CustomerType(IDataStore dataStore) {
Field(c => c.Name);
Field(c => c.BillingAddress);
Field <ListGraphType<OrderType> , IEnumerable<Order>> ()
.Name("Orders")
.ResolveAsync(ctx => {
return dataStore.GetOrdersByCustomerIdAsync(ctx.Source.CustomerId);
});
}
}

为了查询所有的顾客和订单,我们还需要暴露出2个新的节点。所以我们修改在InventoryQuery构造函数中添加如下代码:

InventoryQuery
Field<ListGraphType<OrderType>, IEnumerable<Order>>()
.Name("Orders")
.ResolveAsync(ctx =>
{
return dataStore.GetOrdersAsync();
}); Field<ListGraphType<CustomerType>, IEnumerable<Customer>>()
.Name("Customers")
.ResolveAsync(ctx =>
{
return dataStore.GetCustomersAsync();
});

然后我们需要在IDataStore中定义6个新的方法,并在DataStore中实现它们。

IDataStore
Task<IEnumerable<Order>> GetOrdersAsync();

Task<IEnumerable<Customer>> GetCustomersAsync();

Task<Customer> GetCustomerByIdAsync(int customerId);

Task<IEnumerable<Order>> GetOrdersByCustomerIdAsync(int customerId);

Task<Order> AddOrderAsync(Order order);

Task<Customer> AddCustomerAsync(Customer customer);
DataStore
public async Task<IEnumerable<Order>> GetOrdersAsync()
{
return await _context.Orders
.AsNoTracking()
.ToListAsync();
} public async Task<IEnumerable<Customer>> GetCustomersAsync()
{
return await _context.Customers
.AsNoTracking()
.ToListAsync();
} public async Task<Customer> GetCustomerByIdAsync(int customerId)
{
return await _context.Customers
.FindAsync(customerId);
} public async Task<IEnumerable<Order>> GetOrdersByCustomerIdAsync(int customerId)
{
return await _context.Orders
.Where(o => o.CustomerId == customerId)
.ToListAsync();
} public async Task<Order> AddOrderAsync(Order order)
{
var addedOrder = await _context.Orders.AddAsync(order);
await _context.SaveChangesAsync();
return addedOrder.Entity;
} public async Task<Customer> AddCustomerAsync(Customer customer)
{
var addedCustomer = await _context.Customers.AddAsync(customer);
await _context.SaveChangesAsync();
return addedCustomer.Entity;
}

添加完以上代码之后,我们就需要定义添加订单和顾客的输入类型了。还记得在上一章中我们如何添加货物的么?我们添加了一个ItemInputType类,定义了添加货物需要收集的字段,所以这里同理,我们也需要为订单和顾客定义对应的InputObjectGraphType

OrderInputType
public class OrderInputType : InputObjectGraphType {
public OrderInputType()
{
Name = "OrderInput";
Field<NonNullGraphType<StringGraphType>>("tag");
Field<NonNullGraphType<DateGraphType>>("createdAt");
Field<NonNullGraphType<IntGraphType>>("customerId");
}
}
CustomerInputType
public class CustomerInputType : InputObjectGraphType {
public CustomerInputType()
{
Name = "CustomerInput";
Field<NonNullGraphType<StringGraphType>>("name");
Field<NonNullGraphType<StringGraphType>>("billingAddress");
}
}

当前添加以上代码之后,我们还需要在Startup类中注册这几个新类型

public void ConfigureServices(IServiceCollection services)
{
....
....
services.AddScoped<CustomerType>();
services.AddScoped<CustomerInputType>();
services.AddScoped<OrderType>();
services.AddScoped<OrderInputType>();
}

如果现在启动项目,你会得到以下错误

Failed to call Activator.CreateInstance. Type: chapter1.OrderType

这里的问题是在InventorySchema构造函数中的注入没起作用, 原因是GraphQL在解决依赖的时候,只能处理一层, 这里OrderTypeCustomerType是2层的关系。如果想解决这个问题,我们需要在Startup中再注册一个依赖解决器。

services.AddScoped<IDependencyResolver>(s =>
new FuncDependencyResolver(s.GetRequiredService));

修改完成之后我们还需要修改InventorySchema, 在构造函数中将依赖解决器注入。

public class InventorySchema: Schema {
public InventorySchema(IDependencyResolver resolver): base(resolver) {
Query = resolver.Resolve<InventoryQuery>();
Mutation = resolver.Resolve<InventoryMutation>();
}
}

现在再次启动项目,程序不报错了。

最终效果

下面我们首先创建一个Customer

然后我们继续创建2个Order



最后我们来查询一下刚才创建的数据是否存在

数据读取正确,这说明我们的数据添加成功了。

本文源代码: https://github.com/lamondlu/GraphQL_Blogs/tree/master/Part%20VIII

ASP.NET Core中使用GraphQL - 第八章 在GraphQL中处理一对多关系的更多相关文章

  1. 【新书推荐】《ASP.NET Core微服务实战:在云环境中开发、测试和部署跨平台服务》 带你走近微服务开发

    <ASP.NET Core 微服务实战>译者序:https://blog.jijiechen.com/post/aspnetcore-microservices-preface-by-tr ...

  2. ASP.NET Core的路由[1]:注册URL模式与HttpHandler的映射关系

    ASP.NET Core的路由是通过一个类型为RouterMiddleware的中间件来实现的.如果我们将最终处理HTTP请求的组件称为HttpHandler,那么RouterMiddleware中间 ...

  3. 【无私分享:ASP.NET CORE 项目实战(第八章)】读取配置文件(二) 读取自定义配置文件

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 我们在 读取配置文件(一) appsettings.json 中介绍了,如何读取appsettings.json. 但随之产生 ...

  4. 在ASP dot Net Core MVC中用Controllers调用你的Asp dotnet Core Web API 实现CRUD到远程数据库中,构建你的分布式应用(附Git地址)

    本文所有的东西都是在dot Net Core 1.1环境+VS2017保证测试通过. 本文接着上次文章接着写的,不了解上篇文章的可能看着有点吃力.我尽量让大家都能看懂.这是上篇文章的连接http:// ...

  5. ASP.NET Core WebApi 返回统一格式参数(Json 中 Null 替换为空字符串)

    相关博文:ASP.NET Core WebApi 返回统一格式参数 业务场景: 统一返回格式参数中,如果包含 Null 值,调用方会不太好处理,需要替换为空字符串,示例: { "respon ...

  6. 【ASP.NET Core】在Win 10 的 Linux 子系统中安装 .NET Core

    在上一篇文章中,老周扯了一下在 Windows 10 中开启 Linux 子系统,并且进行了一些简单的设置.本篇咱们就往上面安装 .net core . 老周假设你从来没有用过 Linux,所以,接着 ...

  7. 学习ASP.NET Core Razor 编程系列十二——在页面中增加校验

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  8. ASP.NET Core SameSite 设置引起 Cookie 在 QQ 浏览器中不起作用

    最近在发布了基于 ASP.NET Core 实现的新版登录页面之后,陆陆续续地接到用户反馈登录时 Antiforgery Token 总是验证失败. 日志中记录的对应错误是 Antiforgery t ...

  9. ASP.NET Core 3.0 : 二十八. 在Docker中的部署以及docker-compose的使用

    本文简要说一下ASP.NET Core 在Docker中部署以及docker-compose的使用  (ASP.NET Core 系列目录). 系统环境为CentOS 8 . 打个广告,求职中.. 一 ...

随机推荐

  1. python之six用法

    six.PY2 返回一个表示当前运行环境是否为python2的boolean值 six.PY3 返回一个表示当前运行环境是否为python3的boolean值 import six,sys print ...

  2. Python cmp() 函数

    描述 cmp(x,y) 函数用于比较2个对象,如果 x < y 返回 -1, 如果 x == y 返回 0, 如果 x > y 返回 1. 语法 以下是 cmp() 方法的语法:cmp( ...

  3. CMake入门实战

    本文用来记录基本的Cmake用法,以一个实例,讲解如何通过cmake构建一个一个基本的工程,文件的目录如下: 说明: bin文件夹下的debug和release分别存放编译输出的文件和相关依赖的动态库 ...

  4. IOT高性能服务器实现之路

    市场动态 物联网市场在2018年第一季度/第二季度出现了意想不到的加速,并将使用的物联网设备总数提升至7B.这是IoT Analytics最新“ 物联网和短期展望状态 ”更新中的众多发现之一. 全面的 ...

  5. 基于JavaMail开发邮件发送器工具类

    基于JavaMail开发邮件发送器工具类 在开发当中肯定会碰到利用Java调用邮件服务器的服务发送邮件的情况,比如账号激活.找回密码等功能.本人之前也碰到多次这样需求,为此特意将功能封装成一个简单易用 ...

  6. SSRS报表服务随笔(rdl报表服务)-报表参数

    上一篇我们说了创建一个简单的显示报表,但在实际工作中,我们有很多要带条件的报表 现在先认识一下报表数据,首次打开SSDT,报表数据在窗口的左侧,要是找不到了,没关系,在工具栏-视图-最下面的报表数据 ...

  7. C#ComboBox控件“设置 DataSource 属性后无法修改项集合”的解决方法

    在使用ComboBox控件时,遇到了重新绑定赋值出问题的情况.正常情况下,对于数据重新赋值的或者绑定数据源的时候,为了防止数据出现问题,都会先清空原来数据,所以就这样写了,但是没有相当恰恰这样写就出现 ...

  8. TensorFlow从1到2(三)数据预处理和卷积神经网络

    数据集及预处理 从这个例子开始,相当比例的代码都来自于官方新版文档的示例.开始的几个还好,但随后的程序都将需要大量的算力支持.Google Colab是一个非常棒的云端实验室,提供含有TPU/GPU支 ...

  9. Spring-Data-JPA尝鲜:快速搭建CRUD+分页后台实例

    前言:由于之前没有接触过Hibernate框架,但是最近看一些博客深深被它的"效率"所吸引,所以这就来跟大家一起就着一个简单的例子来尝尝Spring全家桶里自带的JPA的鲜 Spr ...

  10. 海康相机SDK二次开发只有视频无声音问题

    海康SDK相信做企业开发的的同仁,在项目中经常会用到,毕竟使用范围这么广. 本次就开发遇到的奇葩问题来说明一下我们的解决方案. 场景 虽然海康有4200客户端,但是对于高度定制化的项目,肯定不能再使用 ...