今天,我们引入两个新的实体来处理客户与订单。客户与订单之间是一对多的关系,一个客户可以拥有一个或者多个订单,反过来,一个订单只能被某个客户所拥有。

可以按照 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: 使用一对多查询的更多相关文章

  1. mybatis0205 一对多查询 复杂

    查询所有用户信息,关联查询订单及订单明细信息及商品信息,订单明细信息中关联查询商品信息 1.1sql 主查询表:用户信息 关联查询:订单.订单明细,商品信息 SELECT orders.*, user ...

  2. mybatis0204 一对多查询

    查询所有订单信息及订单下的订单明细信息. sql语句 主查询表:订单表 关联查询表:订单明细 SELECT orders.*, user.username, user.sex , orderdetai ...

  3. Mybatis一对多查询得不到多方结果

    一对多查询:一个年级对应多个学生,现在要查询年级(带学生)信息. 查询结果: [main] INFO com.java1234.service.GradeTest - 查询年级(带学生)[main] ...

  4. 非关心数据库无法进行连表查询 所以我们需要在进行一对多查询时候 无法满足 因此需要在"1"的一方添加"多"的一方的的id 以便用于进行连表查询 ; 核心思想通过id进行维护与建文件

     非关心数据库无法进行连表查询 所以我们需要在进行一对多查询时候 无法满足 因此需要在"1"的一方添加"多"的一方的的id 以便用于进行连表查询 ; 核心思想通 ...

  5. flask的orm框架(SQLAlchemy)-一对多查询以及多对多查询

    一对多,多对多是什么? 一对多.例如,班级与学生,一个班级对应多个学生,或者多个学生对应一个班级. 多对多.例如,学生与课程,可以有多个学生修同一门课,同时,一门课也有很多学生. 一对多查询 如果一个 ...

  6. sql 一对多查询

    1. 一对多查询 查询departmentinfo字典下所有部门的人员数量 select * from departmentinfo a left join (select count(*) User ...

  7. mybatis的一对一,一对多查询,延迟加载,缓存介绍

    一对一查询 需求 查询订单信息关联查询用户信息 sql语句 /*通过orders关联查询用户使用user_id一个外键,只能关联查询出一条用户记录就可以使用内连接*/ SELECT orders.*, ...

  8. mybatis由浅入深day02_3一对多查询

    3 一对多查询 3.1 需求(查询订单及订单明细的信息) 查询订单及订单明细的信息. 3.2 sql语句 确定主查询表:订单表 确定关联查询表:订单明细表 在一对一查询基础上添加订单明细表关联即可. ...

  9. Excel一对多查询(index+small+if)

    一.学习 一对多查询模式化数组公式: =INDEX(区域,SMALL(IF(条件,行号数组,4^8),ROW(A1))) 三键齐按(ctrl+shift+回车) 在具有多个符合条件的情况下,提取和匹配 ...

  10. hibernate一对多查询

    一对多查询 1,同时添加老师和学生案例 在进行具有关联关系的对象同时添加时 首先绑定对像间的关系 ---将多方关联一方 ---将一方关联多方 然后全部添加 备注: 1,保存老师对象时, 由于设置了学生 ...

随机推荐

  1. 通过MobaXterm操作远程服务器电脑的方法

      本文介绍在Windows电脑中,下载.配置MobaXterm软件,从而连接.操作远程服务器的方法.   因为是在Windows操作系统的电脑中连接服务器,所以建议使用MobaXterm.PuTTY ...

  2. HBase集群安装及集成

    Hbase集群搭建 注意:在安装hbase或者hadoop的时候,要注意hadoop和hbase的对应关系.如果版本不对应可能造成系统的不稳定和一些其他的问题.在hbase的lib目录下可以看到had ...

  3. PHP面试,拼团

    如何设计数据库模型来支持拼团功能? 答案:拼团功能涉及到多个用户参与同一团的情况,可以设计以下表结构: Product 表: 存储商品信息,包括商品ID.名称.价格等字段. Group 表: 存储拼团 ...

  4. ajax发送 put和delete请求

    AJAX发送 PUT和DELETE请求(转载)    转自:https://blog.csdn.net/liuyuanjiang109/article/details/78972644 ajax使用r ...

  5. qemu的使用

    一.QEMU的运行模式 直接摘抄自己<揭秘家用路由器0day漏洞挖掘技术>,网上查了一下也没有找到令人满意的QEMU的使用说明,就采用这本书上的介绍.如果后期能够找到比较满意的QEMU的使 ...

  6. python:将文件从一个目录移动到另一个目录。附:nnUnet使用

    在使用nn-Unet做BraTS2019数据集预测时,预测文件分别生成了三类文件:.pkl  .npz  .nii.gz,我们需要的是.nii.gz文件.所以需要进行文件移动. # coding:ut ...

  7. C#获取环境变量的值

    Environment.GetEnvironmentVariable("Path"); 修改环境变量之后,不能立即生效,需要重启一下VStudio 或重启电脑 : 在 vstudi ...

  8. slot原理

    vue提供组件插槽能力, 允许开发者在封装组件时,把不确定的部分定义为插槽 : 作用:再不确定的内容位置占位,在子组件内使用 slot 标签写占位的内容 : 插槽的分类 : 默认插槽: 具名插槽:使用 ...

  9. 谈一谈 vuex 中的核心属性

    1. state 保存数据的位置 : 2. mutations 唯一修改 state 数据的方式 : 3. getter 监听 state 数据的变化 : 4. actions 执行异步代码,通过 c ...

  10. JS转义html编码

    两个方法: 1.利用用浏览器内部转换器实现html转义: 2.用正则表达式实现html转义: var HtmlUtil = { /*1.用浏览器内部转换器实现html编码(转义)*/ htmlEnco ...