使用HotChocolate和.NET6构建GraphQL应用(2)——实体相关功能实现

博客 分享
0 319
张三
张三 2022-01-24 16:56:00
悬赏:0 积分 收藏

使用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

本文来自博客园,作者:CODE4NOTHING,转载请注明原文链接:https://www.cnblogs.com/code4nothing/p/graphql-net6-2.html

posted @ 2022-01-24 15:56 CODE4NOTHING 阅读(10) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员