使用Hot Chocolate和.NET 6构建GraphQL应用(2) —— 实体相关功能实现
系列导航
使用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特性,在系列文章中会以第二种和第三种方式为主。
参考文章
- Hot Chocolate Document
- A Demo On Hot Chocolate GraphQL Integration In Asp.Net Core Application Using Dapper Micro ORM
使用Hot Chocolate和.NET 6构建GraphQL应用(2) —— 实体相关功能实现的更多相关文章
- 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引
系列背景 在进入微服务的实践系列之前,我们一起来学习和实践一下.NET应用开发生态中一些比较重要的技术,这个系列就是关于GraphQL在.NET 6应用中的实现. 系列导航 使用Hot Chocola ...
- 使用Hot Chocolate和.NET 6构建GraphQL应用(1)——GraphQL及示例项目介绍
系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 前言 这篇文章是这个系列的第一篇,我们会简单地讨论一下GraphQL,然后介绍一下这个系列将会使用的示例项目. 关 ...
- 使用Hot Chocolate和.NET 6构建GraphQL应用(3) —— 实现Query基础功能
系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在本文中,我们通过一个简单的例子来看一下如何实现一个最简单的GraphQL的接口. 实现 引入Hot Cho ...
- 使用Hot Chocolate和.NET 6构建GraphQL应用(4) —— 实现Query映射功能
系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在上一篇文章使用Hot Chocolate和.NET 6构建GraphQL应用(3) -- 实现Query基 ...
- 使用Hot Chocolate和.NET 6构建GraphQL应用(5) —— 实现Query过滤功能
系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 对于查询来说,还有一大需求是针对查询的数据进行过滤,本篇文章我们准备实现GraphQL中基本的查询过滤. 思 ...
- 使用Hot Chocolate和.NET 6构建GraphQL应用(6) —— 实现Query排序功能
系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 从前几篇文章可以看出,使用Hot Chocolate实现GraphQL接口是比较简单的,本篇文章我们继续查询 ...
- 使用Hot Chocolate和.NET 6构建GraphQL应用(7) —— 实现Query分页功能
系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 GraphQL中的查询分页相对来说是查询中比较难理解的,接口的Schema也和其他不一样.在这篇文章中,我们 ...
- 使用Hot Chocolate和.NET 6构建GraphQL应用(8) —— 实现Mutate添加数据
系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在讨论完GraphQL中的查询需求后,这篇文章我们将演示如何实现GraphQL中的数据添加任务. 思路 在G ...
- 使用Hot Chocolate和.NET 6构建GraphQL应用(9) —— 实现Mutate更新数据
系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 需求 在上一篇文章中,我们演示了如何使用Hot Chocolate进行GraphQL的Mutate新增数据,这篇文 ...
随机推荐
- 串口之完整封装包含发送和接收(windows+ubuntu已通过初步测试)(持续更新)
这里下载源码 更新日志 16-08-2021 V1.0.3 1.修复接收数据没有将数据传递给应用层的bug 2.windows版本:设置接收数据相邻字节间间隔为5ms 24-09-2020 V1.0. ...
- 【LeetCode】146. LRU Cache 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典+双向链表 日期 题目地址:https://le ...
- 【LeetCode】336. Palindrome Pairs 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 HashTable 相似题目 参考资料 日期 题目地 ...
- 【LeetCode】238. Product of Array Except Self 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 解题方法 两次遍历 日期 题目地址:https://leetcode.c ...
- 【LeetCode】421. Maximum XOR of Two Numbers in an Array 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 解题方法 依次遍历每一位 前缀树 日期 题目地址:https://lee ...
- delete、truncate、drop
DELETE DELETE属于数据库DML操作语言,只删除数据不删除表的结构,会走事务,执行时会触发trigger:每次从表中删除一行,并且同时将该行的的删除操作记录在redo和undo表空间中以便进 ...
- 第二十七个知识点:什么是对称密码加密的AEAD安全定义?
第二十七个知识点:什么是对称密码加密的AEAD安全定义? AEAD 在之前的博客里,Luke描述了一种被广泛使用的操作模式(ECB,CBC和CTR)对块密码.我们也可能会想我们加密方案的完整性,完整性 ...
- Java初学者作业——为某超市设计管理系统,需要在控制台展示系统菜单,菜单之间可以完成跳转。
返回本章节 返回作业目录 需求说明: 为某超市设计管理系统,需要在控制台展示系统菜单,菜单之间可以完成跳转. 实现思路: 定义mainMenu方法,用于显示主菜单. 主菜单主要负责显示4个选项,分别是 ...
- 编写Java程序,使用Set实现不重复添加用户
返回本章节 返回作业目录 需求说明: 在控制台输入用户信息,用户信息包括姓名.性别和年龄,将用户信息保存至User对象中. 将User对象保存至HashSet集合中. 规定如果两个User对象的姓名. ...
- Log4j2进阶使用(按大小时间备份日志)
1.进阶说明 本文介绍Log4j2进阶使用, 基本使用请参考Log4j2基本使用入门. 本文基于上面的基本使用入门, 主要介绍按照日志大小和时间备份日志, 并且限制备份日志的个数, 以及删除过期的备份 ...