GraphQL Part VIII: 使用一对多查询
今天,我们引入两个新的实体来处理客户与订单。客户与订单之间是一对多的关系,一个客户可以拥有一个或者多个订单,反过来,一个订单只能被某个客户所拥有。
可以按照 Engity Framework 的约定配置实体之间的关系。 如果某个实体拥有一个第二个实体的集合属性,Entity Framework 会自动创建一对多的关系。该属性被称为导航属性。
在 Customer 实体中有一个 Orders 属性,作为集合类型的导航属性。
Customer.cs
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.cs
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; }
}
一旦定义了所有需要的关系,就可以使用 dotnet CLI 创建一个 migration 然后更新数据库。
dotnet ef migrations add OneToManyRelationship
dotnet ef database update
我们还需要创建两个新的 ObjectGraphType 。
OrderType.cs
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);
});
}
}
为了暴露两个新的端点,还需要在 InventoryQuery 中注册这两个类型。
InventoryQuery.cs
Field<ListGraphType<OrderType>, IEnumerable<Order>>()
.Name("Orders")
.ResolveAsync(ctx =>
{
return dataStore.GetOrdersAsync();
}); Field<ListGraphType<CustomerType>, IEnumerable<Customer>>()
.Name("Customers")
.ResolveAsync(ctx =>
{
return dataStore.GetCustomersAsync();
});
在后台的数据仓库中,还需要提供相应的数据访问方法。
DataStore.cs
public async Task <IEnumerable<Order>> GetOrdersAsync() {
return await _applicationDbContext.Orders.AsNoTracking().ToListAsync();
}
public async Task <IEnumerable<Customer>> GetCustomersAsync() {
return await _applicationDbContext.Customers.AsNoTracking().ToListAsync();
}
public async Task <Customer> GetCustomerByIdAsync(int customerId) {
return await _applicationDbContext.Customers.FindAsync(customerId);
}
public async Task <IEnumerable<Order>> GetOrdersByCustomerIdAsync(int customerId) {
return await _applicationDbContext.Orders.Where(o => o.CustomerId == customerId).ToListAsync();
}
同时,我们还增加两个方法用于创建 Customer 和 Order,
public async Task<Order> AddOrderAsync(Order order)
{
var addedOrder = await _applicationDbContext.Orders.AddAsync(order);
await _applicationDbContext.SaveChangesAsync();
return addedOrder.Entity;
} public async Task<Customer> AddCustomerAsync(Customer customer)
{
var addedCustomer = await _applicationDbContext.Customers.AddAsync(customer);
await _applicationDbContext.SaveChangesAsync();
return addedCustomer.Entity;
}
在上一篇 Blog 中,我们创建过 InputObjectGraphType 用于 Item 的创建,与其类似,我们也需要为 Customer 和 Order 创建对应的 InputObjectGraph。
OrderInputType.cs
public class OrderInputType : InputObjectGraphType {
public OrderInputType()
{
Name = "OrderInput";
Field<NonNullGraphType<StringGraphType>>("tag");
Field<NonNullGraphType<DateGraphType>>("createdAt");
Field<NonNullGraphType<IntGraphType>>("customerId");
}
}
CustomerInputType.cs
public class CustomerInputType : InputObjectGraphType {
public CustomerInputType()
{
Name = "CustomerInput";
Field<NonNullGraphType<StringGraphType>>("name");
Field<NonNullGraphType<StringGraphType>>("billingAddress");
}
}
最后,我们需要注册所有新的的类型到 DI 系统中。在 Startup 的 ConfigureServices 方法中如下注册。
public void ConfigureServices(IServiceCollection services)
{
....
....
services.AddScoped<CustomerType>();
services.AddScoped<CustomerInput>();
services.AddScoped<OrderType>();
services.AddScoped<OrderInputType>();
}
现在,如果您运行应用,将会看到如下的错误信息:
"No parameterless constructor defined for this object."

通过调查 graphql-dotnet,我发现了这个问题:issue
对于当前的方案, Schema 的构造函数注入不能工作。DI 系统可以获取类型一次,但是不能对该对象链的子级再次获取。简单来说,如果在 InventoryQuery 中通过构造函数注入了 IDataSource 一次 ,但是,你不能在其他的 Graph Type 构造函数中注入;例如 CustomerType。但是,这不是我们期望的行为,因此,使用 IDependencyResolver,在 DI 系统中注册 IDependencyResolver,并确保提供一个有限的生命周期。
services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService));
需要在 InventorySchemacs 中做一点修改,修改代码,在构造函数中注入 IDependencyResolver。
InventorySchema.cs
public class InventorySchema: Schema {
public InventorySchema(IDependencyResolver resolver): base(resolver) {
Query = resolver.Resolve < InventoryQuery > ();
Mutation = resolver.Resolve < InventoryMutation > ();
}
}
现在,重新运行应用,并验证你可以访问新增加的字段。

GraphQL Part VIII: 使用一对多查询的更多相关文章
- mybatis0205 一对多查询 复杂
查询所有用户信息,关联查询订单及订单明细信息及商品信息,订单明细信息中关联查询商品信息 1.1sql 主查询表:用户信息 关联查询:订单.订单明细,商品信息 SELECT orders.*, user ...
- mybatis0204 一对多查询
查询所有订单信息及订单下的订单明细信息. sql语句 主查询表:订单表 关联查询表:订单明细 SELECT orders.*, user.username, user.sex , orderdetai ...
- Mybatis一对多查询得不到多方结果
一对多查询:一个年级对应多个学生,现在要查询年级(带学生)信息. 查询结果: [main] INFO com.java1234.service.GradeTest - 查询年级(带学生)[main] ...
- 非关心数据库无法进行连表查询 所以我们需要在进行一对多查询时候 无法满足 因此需要在"1"的一方添加"多"的一方的的id 以便用于进行连表查询 ; 核心思想通过id进行维护与建文件
非关心数据库无法进行连表查询 所以我们需要在进行一对多查询时候 无法满足 因此需要在"1"的一方添加"多"的一方的的id 以便用于进行连表查询 ; 核心思想通 ...
- flask的orm框架(SQLAlchemy)-一对多查询以及多对多查询
一对多,多对多是什么? 一对多.例如,班级与学生,一个班级对应多个学生,或者多个学生对应一个班级. 多对多.例如,学生与课程,可以有多个学生修同一门课,同时,一门课也有很多学生. 一对多查询 如果一个 ...
- sql 一对多查询
1. 一对多查询 查询departmentinfo字典下所有部门的人员数量 select * from departmentinfo a left join (select count(*) User ...
- mybatis的一对一,一对多查询,延迟加载,缓存介绍
一对一查询 需求 查询订单信息关联查询用户信息 sql语句 /*通过orders关联查询用户使用user_id一个外键,只能关联查询出一条用户记录就可以使用内连接*/ SELECT orders.*, ...
- mybatis由浅入深day02_3一对多查询
3 一对多查询 3.1 需求(查询订单及订单明细的信息) 查询订单及订单明细的信息. 3.2 sql语句 确定主查询表:订单表 确定关联查询表:订单明细表 在一对一查询基础上添加订单明细表关联即可. ...
- Excel一对多查询(index+small+if)
一.学习 一对多查询模式化数组公式: =INDEX(区域,SMALL(IF(条件,行号数组,4^8),ROW(A1))) 三键齐按(ctrl+shift+回车) 在具有多个符合条件的情况下,提取和匹配 ...
- hibernate一对多查询
一对多查询 1,同时添加老师和学生案例 在进行具有关联关系的对象同时添加时 首先绑定对像间的关系 ---将多方关联一方 ---将一方关联多方 然后全部添加 备注: 1,保存老师对象时, 由于设置了学生 ...
随机推荐
- MySQL配置缓存查询和维护
配置文件设置缓存 query_cache_size = 20M #缓存大小 query_cache_type = ON #开启缓存 table_cache=256 #用于限制缓存表的最大数目,如果当前 ...
- 【赵渝强老师】Kafka的消息持久化
1.Kafka消息持久性概述 Kakfa依赖文件系统来存储和缓存消息.对于硬盘的传统观念是硬盘总是很慢,基于文件系统的架构能否提供优异的性能?实际上硬盘的快慢完全取决于使用方式.同时 Kafka 基于 ...
- SQL数据库书的配套资源
<SQL基础教程(视频教学版)>代码.课件.教学视频 https://pan.baidu.com/s/1QA5m5HN159wi3FX_KNDQzA 提取码: 4m9e <SQL S ...
- 在 macOS上安装 MongoDB 社区版
官网教程 https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/ 使用第三方 brew package manager 在 ...
- LeetCode 650. 2 Keys Keyboard(只有两个键的键盘)(DP/质因数分解)
最初在一个记事本上只有一个字符 'A'.你每次可以对这个记事本进行两种操作: Copy All (复制全部) : 你可以复制这个记事本中的所有字符(部分的复制是不允许的). Paste (粘贴) : ...
- `std::string_view`(c++17) 和 `std::stringstream` 使用区别·
std::string_view 和 std::stringstream 都是 C++ 中处理字符串的工具,但它们的设计目标和使用场景非常不同.我们可以通过几方面进行对比. 1. 设计目的和核心功能 ...
- USB TCPM
USB TCPM(Type-C Port Manager)的主要作用是管理 USB Type-C 端口的连接和电源传输协议(USB Power Delivery, PD),确保设备正确识别.协商和切换 ...
- kali Linux 启动 apache 和 mysqll
kali linux 自带 apache 服务 和 mysql 服务 # 启动 apache 服务 service apache2 start # 启动 mysql 服务 service mysql ...
- day01-markdown学习
markdown学习 标题 两个#号 三级标题 四级标题 字体 hello world!两个*加粗 hello world!一个*斜体 hello world!三个*斜体加粗 hello world! ...
- 二、java之面向对象
面向对象 面向对象编程(Object-Oriented Programming,OOP) 面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据 三大特性: ◆封装 ◆◆封装的概念 程序 ...