系列导航

使用Hot Chocolate和.NET 6构建GraphQL应用文章索引

需求

在本文中,我们将会准备好用于实现GraphQL接口所依赖的底层数据,为下一篇文章具体实现GraphQL接口做准备。

实现

实体定义

在上一篇文章使用Hot Chocolate和.NET 6构建GraphQL应用(1) —— GraphQL及示例项目介绍我们给出的实体关系图稍微进行一些简化,去掉了关于评论回复的实体定义。我们先来实现关于Post/Comment/Tag的实体以及对应的Configuration:

  • Post.cs
namespace PostGraphi.Domain.Post.Entities;

public class Post : AuditableEntity, IEntity<Guid>, IHasDomainEvent
{
public Guid Id { get; set; }
public string? Title { get; set; }
public string? Author { get; set; }
public string? Abstraction { get; set; }
public string? Content { get; set; }
public string? Link { get; set; }
public DateTime PublishedAt { get; set; } public ICollection<Tag> Tags { get; set; } = new HashSet<Tag>();
public ICollection<Comment> Comments { get; set; } = new List<Comment>(); public List<DomainEvent> DomainEvents { get; set; } = new();
}
  • Comment.cs
namespace PostGraphi.Domain.Post.Entities;

public class Comment : IEntity<Guid>
{
public Guid Id { get; set; }
public string? Content { get; set; }
public string? Name { get; set; }
public DateTime CreatedAt { get; set; } public Guid PostId { get; set; }
public Post Post { get; set; }
}
  • Tag.cs
namespace PostGraphi.Domain.Post.Entities;

public class Tag : IEntity<Guid>
{
public Guid Id { get; set; }
public string? Name { get; set; } public ICollection<Post> Posts { get; set; } = new HashSet<Post>();
}
  • PostConfiguration.cs
namespace PostGraphi.Infrastructure.Persistence.Configurations;

public class PostConfiguration : IEntityTypeConfiguration<Post>
{
public void Configure(EntityTypeBuilder<Post> builder)
{
builder.Ignore(e => e.DomainEvents); builder.Property(t => t.Title).HasMaxLength(200).IsRequired(); builder.HasMany(t => t.Tags).WithMany(a => a.Posts);
builder.HasMany(t => t.Comments).WithOne(c => c.Post).HasForeignKey(c => c.PostId);
}
}
  • CommentConfiguration.cs
namespace PostGraphi.Infrastructure.Persistence.Configurations;

public class CommentConfiguration : IEntityTypeConfiguration<Comment>
{
public void Configure(EntityTypeBuilder<Comment> builder)
{
builder.Property(t => t.Content).HasMaxLength(255).IsRequired(); builder.HasOne(t => t.Post).WithMany(a => a.Comments).HasForeignKey(t=>t.PostId);
}
}
  • TagConfiguration.cs
namespace PostGraphi.Infrastructure.Persistence.Configurations;

public class TagConfiguration : IEntityTypeConfiguration<Tag>
{
public void Configure(EntityTypeBuilder<Tag> builder)
{
builder.Property(t => t.Name).HasMaxLength(30).IsRequired(); builder.HasMany(t => t.Posts).WithMany(a => a.Tags);
}
}

并在DbContext类中添加:

  • PostGraphiDbContext.cs
public DbSet<Post> Posts => Set<Post>();
public DbSet<Tag> Tags => Set<Tag>();
public DbSet<Comment> Comments => Set<Comment>();

数据库注入

我们需要修改模版里默认的数据库注入的方式,为了演示重点内容起见,我将数据库修改为SQLite:

  • InfrastructureDependencyInjections.cs
// 省略其他...
// 为了注入数据库实例时还能在构造函数中使用依赖注入
services.AddEntityFrameworkSqlite();
// 使用AddPooledDbContextFactory进行数据库的注入,因为在GraphQL的并发请求下,直接AddDbContext在执行时会报错。
services.AddPooledDbContextFactory<PostGraphiDbContext>((serviceProvider, options) =>
{
options.UseSqlite("Data Source=PostGraphi.db");
// 允许在DbContext的构造函数中使用依赖注入容器
options.UseInternalServiceProvider(serviceProvider);
});

准备种子数据

修改PostGraphiDbContextSeed内容,准备一些种子数据:

  • PostGraphiDbContextSeed.cs
public static async Task SeedSampleDataAsync(PostGraphiDbContext context)
{
if (!context.Posts.Any())
{
var posts = new List<Post>
{
new()
{
Title = "1 - introduction to graphql",
Abstraction = "this is an introduction post for graphql",
Content = "some random content for post 1",
Author = "code4nothing",
PublishedAt = DateTime.Now.AddDays(-2),
Link = "http://link-to-post-1.html",
Comments = new List<Comment>
{
new() { CreatedAt = DateTime.Now.AddHours(-3), Content = "comment 01 for post 1", Name = "kindUser01" },
new() { CreatedAt = DateTime.Now.AddHours(-2), Content = "comment 02 for post 1", Name = "kindUser01" },
new() { CreatedAt = DateTime.Now, Content = "comment 03 for post 1", Name = "kindUser02" }
},
Tags = new List<Tag>
{
new() { Name = "graphql" },
new() { Name = ".net6" }
}
},
new()
{
Title = "2 - integrate graphql with hot chocolate to .net6",
Abstraction = "this is an introduction post for how to integrate graphql to .net6",
Content = "some random content for post 2",
Author = "code4nothing",
PublishedAt = DateTime.Now.AddDays(-1),
Link = "http://link-to-post-2.html",
Comments = new List<Comment>
{
new() { CreatedAt = DateTime.Now.AddHours(-5), Content = "comment 01 for post 2", Name = "kindUser02" },
new() { CreatedAt = DateTime.Now.AddHours(-1), Content = "comment 02 for post 2", Name = "kindUser03" },
new() { CreatedAt = DateTime.Now, Content = "comment 03 for post 2", Name = "kindUser04" }
},
Tags = new List<Tag>
{
new() { Name = "graphql" },
new() { Name = ".net6" },
new() { Name = "hot chocolate" }
}
},
new()
{
Title = "3 - use Dapr with .net6",
Abstraction = "this is an introduction post for how to use dapr in .net6 applications",
Content = "some random content for post 3",
Author = "code4dapr",
PublishedAt = DateTime.Now.AddDays(-1),
Link = "http://link-to-post-3.html",
Comments = new List<Comment>
{
new() { CreatedAt = DateTime.Now.AddHours(-3), Content = "comment 01 for post 3", Name = "kindUser01" },
new() { CreatedAt = DateTime.Now.AddHours(-2), Content = "comment 02 for post 3", Name = "kindUser02" },
new() { CreatedAt = DateTime.Now, Content = "comment 03 for post 3", Name = "kindUser04" },
new() { CreatedAt = DateTime.Now, Content = "comment 04 for post 3", Name = "kindUser03" }
},
Tags = new List<Tag>
{
new() { Name = "dapr" },
new() { Name = ".net6" }
}
},
new()
{
Title = "4 - use dapr service invocation in .net6",
Abstraction = "this is an introduction post for how to use dapr service invocation in .net6",
Content = "some random content for post 4",
Author = "code4dapr",
PublishedAt = DateTime.Now.AddDays(-1),
Link = "http://link-to-post-4.html",
Comments = new List<Comment>
{
new() { CreatedAt = DateTime.Now.AddHours(-3), Content = "comment 01 for post 4", Name = "kindUser04" }
},
Tags = new List<Tag>
{
new() { Name = "dapr" },
new() { Name = ".net6" },
new() { Name = "service invocation" }
}
}
}; context.Posts.AddRange(posts);
await context.SaveChangesAsync();
}
}

应用数据库迁移和种子数据生成

  • Program.cs
public static void ApplyDatabaseMigration(this WebApplication app)
{
using var scope = app.Services.CreateScope(); var retryPolicy = CreateRetryPolicy(app.Configuration, Log.Logger);
// 注意因为数据库的注入方式变了,所以获取数据库Context的方法也相应修改
using var context = scope.ServiceProvider.GetRequiredService<IDbContextFactory<PostGraphiDbContext>>().CreateDbContext();
// 应用Migration
retryPolicy.Execute(context.Database.Migrate);
// 生成种子数据
PostGraphiDbContextSeed.SeedSampleDataAsync(context).Wait();
}

执行dotnet migrations add命令行去生成第一次migration数据,运行程序,可以通过数据库工具看到种子数据已经被成功生成到数据库了。

总结

下一篇文章起,我们就开始使用Hot Chocolate来完成GraphQL接口的实现。在进入之前,我想先简单介绍一下在Hot Chocolate中编写GraphQL相关功能的三种方式,为下一节内容作准备:

Schema First

这种实现方式完全采用了GraphQL Schema定义语言,写起来比较繁琐,我们一般不采用这种方式。

Code First

这种方式不需要写Schema,但是每个C#定义的实体类必须有对应Mapping的GraphQL C#类。

Annotation First

不需要写Schema,也不要求有对应的GraphQL C#类,仅仅需要定义的实体类。实现方式比较简单,具体的Schema生成由GraphQL服务器自动实现。

这三种方式可以混搭着使用,为了演示尽量多的Hot Chocolate特性,在系列文章中会以第二种和第三种方式为主。

参考文章

  1. Hot Chocolate Document
  2. A Demo On Hot Chocolate GraphQL Integration In Asp.Net Core Application Using Dapper Micro ORM

使用Hot Chocolate和.NET 6构建GraphQL应用(2) —— 实体相关功能实现的更多相关文章

  1. 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引

    系列背景 在进入微服务的实践系列之前,我们一起来学习和实践一下.NET应用开发生态中一些比较重要的技术,这个系列就是关于GraphQL在.NET 6应用中的实现. 系列导航 使用Hot Chocola ...

  2. 使用Hot Chocolate和.NET 6构建GraphQL应用(1)——GraphQL及示例项目介绍

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 前言 这篇文章是这个系列的第一篇,我们会简单地讨论一下GraphQL,然后介绍一下这个系列将会使用的示例项目. 关 ...

  3. 使用Hot Chocolate和.NET 6构建GraphQL应用(3) —— 实现Query基础功能

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在本文中,我们通过一个简单的例子来看一下如何实现一个最简单的GraphQL的接口. 实现 引入Hot Cho ...

  4. 使用Hot Chocolate和.NET 6构建GraphQL应用(4) —— 实现Query映射功能

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在上一篇文章使用Hot Chocolate和.NET 6构建GraphQL应用(3) -- 实现Query基 ...

  5. 使用Hot Chocolate和.NET 6构建GraphQL应用(5) —— 实现Query过滤功能

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 对于查询来说,还有一大需求是针对查询的数据进行过滤,本篇文章我们准备实现GraphQL中基本的查询过滤. 思 ...

  6. 使用Hot Chocolate和.NET 6构建GraphQL应用(6) —— 实现Query排序功能

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 从前几篇文章可以看出,使用Hot Chocolate实现GraphQL接口是比较简单的,本篇文章我们继续查询 ...

  7. 使用Hot Chocolate和.NET 6构建GraphQL应用(7) —— 实现Query分页功能

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 GraphQL中的查询分页相对来说是查询中比较难理解的,接口的Schema也和其他不一样.在这篇文章中,我们 ...

  8. 使用Hot Chocolate和.NET 6构建GraphQL应用(8) —— 实现Mutate添加数据

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在讨论完GraphQL中的查询需求后,这篇文章我们将演示如何实现GraphQL中的数据添加任务. 思路 在G ...

  9. 使用Hot Chocolate和.NET 6构建GraphQL应用(9) —— 实现Mutate更新数据

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在上一篇文章中,我们演示了如何使用Hot Chocolate进行GraphQL的Mutate新增数据,这篇文 ...

随机推荐

  1. 串口之完整封装包含发送和接收(windows+ubuntu已通过初步测试)(持续更新)

    这里下载源码 更新日志 16-08-2021 V1.0.3 1.修复接收数据没有将数据传递给应用层的bug 2.windows版本:设置接收数据相邻字节间间隔为5ms 24-09-2020 V1.0. ...

  2. 【LeetCode】146. LRU Cache 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典+双向链表 日期 题目地址:https://le ...

  3. 【LeetCode】336. Palindrome Pairs 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 HashTable 相似题目 参考资料 日期 题目地 ...

  4. 【LeetCode】238. Product of Array Except Self 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 解题方法 两次遍历 日期 题目地址:https://leetcode.c ...

  5. 【LeetCode】421. Maximum XOR of Two Numbers in an Array 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 解题方法 依次遍历每一位 前缀树 日期 题目地址:https://lee ...

  6. delete、truncate、drop

    DELETE DELETE属于数据库DML操作语言,只删除数据不删除表的结构,会走事务,执行时会触发trigger:每次从表中删除一行,并且同时将该行的的删除操作记录在redo和undo表空间中以便进 ...

  7. 第二十七个知识点:什么是对称密码加密的AEAD安全定义?

    第二十七个知识点:什么是对称密码加密的AEAD安全定义? AEAD 在之前的博客里,Luke描述了一种被广泛使用的操作模式(ECB,CBC和CTR)对块密码.我们也可能会想我们加密方案的完整性,完整性 ...

  8. Java初学者作业——为某超市设计管理系统,需要在控制台展示系统菜单,菜单之间可以完成跳转。

    返回本章节 返回作业目录 需求说明: 为某超市设计管理系统,需要在控制台展示系统菜单,菜单之间可以完成跳转. 实现思路: 定义mainMenu方法,用于显示主菜单. 主菜单主要负责显示4个选项,分别是 ...

  9. 编写Java程序,使用Set实现不重复添加用户

    返回本章节 返回作业目录 需求说明: 在控制台输入用户信息,用户信息包括姓名.性别和年龄,将用户信息保存至User对象中. 将User对象保存至HashSet集合中. 规定如果两个User对象的姓名. ...

  10. Log4j2进阶使用(按大小时间备份日志)

    1.进阶说明 本文介绍Log4j2进阶使用, 基本使用请参考Log4j2基本使用入门. 本文基于上面的基本使用入门, 主要介绍按照日志大小和时间备份日志, 并且限制备份日志的个数, 以及删除过期的备份 ...