使用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特性,在系列文章中会以第二种和第三种方式为主。

参考文章

  1. Hot Chocolate Document
  2. A Demo On Hot Chocolate GraphQL Integration In Asp.Net Core Application Using Dapper Micro ORM
文章出处:https://www.cnblogs.com/code4nothing/p/graphql-net6-2.html
版权声明:本文为YES开发框架网发布内容,转载请附上原文出处连接
管理员
上一篇:在cshtml文件中使用预编译语法
下一篇:.NET下如何拦截鼠标、键盘消息?Win32NET来帮你
评论列表

发表评论

评论内容
昵称:
验证码:
验证码
关联文章

使用Hot Chocolate.NET 6构建GraphQL应用(2) —— 实体相关功能实现
使用Hot Chocolate.NET 6构建GraphQL应用(3) —— 实现Query基础功能
使用Hot Chocolate.NET 6构建GraphQL应用(4) —— 实现Query映射功能
使用Hot Chocolate.NET 6构建GraphQL应用(1)——GraphQL及示例项目介绍
使用.NET 6开发TodoList应用(24)——实现基于JWT的Identity功能
使用.NET 6开发TodoList应用(30)——实现Docker打包部署
使用.NET 6开发TodoList应用(29)——实现静态字符串本地化功能
使用.NET 6开发TodoList应用(31)——实现基于Github ActionsACI的CI/CD
使用.NET 6开发TodoList应用(26)——实现ConfigurationOption的强类型绑定
使用.NET 6开发TodoList应用(14)——实现查询过滤
使用.NET 6开发TodoList应用(11)——使用FluentValidationMediatR实现接口请求验证
使用.NET 6开发TodoList应用(22)——实现缓存
使用.NET 6开发TodoList应用(28)——实现应用程序健康检查
使用.NET 6开发TodoList应用(6)——使用MediatR实现POST请求
使用.NET 6开发TodoList应用(7)——使用AutoMapper实现GET请求
使用.NET 6开发TodoList应用(25)——实现RefreshToken
使用.NET 6开发TodoList应用(9)——实现PUT请求
使用.NET 6开发TodoList应用(27)——实现API的Swagger文档化
使用.NET 6开发TodoList应用(23)——实现请求限流
使用.NET 6开发TodoList应用(21)——实现API版本控制

热门标签
.NET Core .NET Reactor ag-grid AI发布 api安全 ASP.NET Core C#DLL加密 C#播放声音 C#代码混淆 C#代码加密 ChromeDriver Codex DateTime DBeaver devexpress devTool DLL混淆 edge.js EF EFCore Electron element-ui el-form el-table excel FastReport FileStream FolderBrowerDialog FolderSelectDialog form提交 git gridcontrol gridview input javascript json字符串 JS转换对象JSON jwt JWT授权 linq log Math MCP mitmproxy MVC MySQL Navicat netstat nginx node_modules NSwag Nuget Nuget镜像 number PowerShell pyinstaller python pythoncom python爬虫 python抓包 pywin32 redis Requests-html RestSharp Selenium sql SQL Server Swagger to-cms Visual Studio VSCode vue VueRouter vue路由 VUE页面通讯 Webpack Windows Windows服务 winform wmi xlrd yaml YESCMS YESWEB开发框架 白象 表单提交 播放声音 打开URL 代码混淆 弹窗提醒 端口占用 对象转换 分布式 公共字典 机器码 进程排查 静态资源 开发指南 路由参数 密钥 配置教程 配置文件 权限 人工智能 任务 任务调度 日期间隔 日志 日志记录 省市区 授权验证 数据库 四舍五入 文案 文件读取 文件夹选择 文件目录选择 问题排查 行政区域数据 页面通讯 中间件 CSharp 事务锁 工单系统 并发控制 重复提交 CMS Markdig Markdown markdown-it marked 技术选型 VS Code 开发工具 源代码管理 版本控制 Docker PostgreSQL 时区 部署排查 CMS架构 EF Core 主题系统 二次开发 插件系统 容器 运维命令 镜像清理 Linux NAS 远程挂载 飞牛 fnOS
联系我们
联系电话:15090125178(微信同号)
电子邮箱:garson_zhang@163.com
站长微信二维码
微信二维码