当前位置: 首页 > news >正文

Hot Chocolate 构建 GraphQL .Net Core 服务

Hot Chocolate 是 .NET 平台下的一个开源组件库, 您可以使用它创建 GraphQL 服务, 它消除了构建成熟的 GraphQL 服务的复杂性, Hot Chocolate 可以连接任何服务或数据源,并创建一个有凝聚力的服务,为您的消费者提供统一的 API。

我会在 .NET 应用中使用 Hot Chocolate 组件来构建 GraphQL 服务, 让我们开始吧!

创建服务

  •  创建一个GraphQL服务

        安装nuget包:        

        HotChocolate.AspNetCore // GraphQL -  HotChocolate实现包
        HotChocolate.Data.EntityFramework //HotChocolate-IQueryable 实现包
        HotChocolate.Subscriptions.Redis //redis订阅
        Microsoft.EntityFrameworkCore.SqlServer //orm ef sqlServer
        Microsoft.EntityFrameworkCore.Tools //ef 工具

        builder.Services.AddGraphQLServer()

使用Hot Chocolate

  • 新增一个Query

        builder.Services.AddQueryType<MyQuery>()//一个Query,所有Query写在一起

public class MyQuery{[UseOffsetPaging][UseProjection][UseFiltering][UseSorting]public IQueryable<Superhero> GetSuperheroes([Service] ApplicationDbContext context) =>context.Superheroes;[UseOffsetPaging][UseProjection][UseFiltering][UseSorting]public IQueryable<Movie> GetMovies([Service] ApplicationDbContext context) =>context.Movies;[UseOffsetPaging][UseProjection][UseFiltering][UseSorting]public IQueryable<Superpower> GetSuperpowers([Service] ApplicationDbContext context) =>context.Superpowers;}
  • 使用特性  搜索、排序、投影

         builder.Services.AddProjections()
         .AddFiltering()
        .AddSorting()
        .SetPagingOptions(new PagingOptions
        {
                    MaxPageSize = 10000,
                    DefaultPageSize = 10,
                    IncludeTotalCount = true
        });

  • 创建多个Query,通过ExtendObjectType("Query")关联
.AddQueryType(q => q.Name("Query")).AddTypeExtension<SuperheroQuery>().AddTypeExtension<MovieQuery>().AddTypeExtension<SuperpowerQuery>()[ExtendObjectType("Query")]public class SuperheroQuery{[UseOffsetPaging][UseProjection]//始终显示的数据字段,无论是否查询该字段[UseFiltering][UseSorting][GraphQLDescription("获取超级英雄集合")]public IQueryable<Superhero> GetSuperheroes([Service] ApplicationDbContext context) =>context.Superheroes;}
  • 创建Mutation&自定义错误信息
.AddMutationType(m => m.Name("Mutation")).AddTypeExtension<SuperheroMutation>()[ExtendObjectType("Mutation")]public class SuperheroMutation{public async Task<Boolean> AddSuperheroAsync(SuperheroDto superhero, [Service] ISuperheroRepository _repository, [Service] ITopicEventSender sender){var (Success, KeyId) = await _repository.AddSuperheroAsync(superhero);await sender.SendAsync("SuperheroModified", superhero);return Success;}[Error(typeof(NameTakenException))]public async Task<Boolean> UpdateSuperheroAsync([ID] Guid Id, string name, [Service] ISuperheroRepository _repository, [Service] ITopicEventSender sender){var Success = await _repository.UpdateSuperheroAsync(Id, name);await sender.SendAsync("SuperheroModified", new SuperheroDto{Id = Id,Name = name,Description = "",Height = 0,Movies = null,Superpowers = null});return Success;}}public class NameTakenException : Exception{public NameTakenException(string username): base($"The name {username} is already taken."){}}
  • 创建指令
.AddDirectiveType<ToUpperDirectiveType>().AddType<toLowerDirective>()/// <summary>/// 转大写指令/// </summary>public class ToUpperDirectiveType : DirectiveType{protected override void Configure(IDirectiveTypeDescriptor descriptor){descriptor.Name("toupper");//descriptor.Argument("name").Type<NonNullType<StringType>>();descriptor.Location(DirectiveLocation.Field);//https://chillicream.com/docs/hotchocolate/v13/execution-engine/field-middleware/#field-middleware-as-a-class//中间件 descriptor.Use((next, directive) =>{return async context =>{await next(context);if (context.Result is string str){context.Result = str.ToUpper();}else{context.ReportError("Bad Request.");context.OperationResult.SetResultState(WellKnownContextData.HttpStatusCode, 500);}};});}}/// <summary>/// 转小写指令/// 属性模式/// </summary>[DirectiveType(DirectiveLocation.Field)][toLowerDirectiveMiddleware]public class toLowerDirective{}/// <summary>/// 指令中间件/// </summary>public class toLowerDirectiveMiddlewareAttribute : DirectiveTypeDescriptorAttribute{protected override async void OnConfigure(IDescriptorContext context, IDirectiveTypeDescriptor descriptor, Type type){descriptor.Use((next, directive) =>{return async context =>{await next(context);if (context.Result is string str){context.Result = str.ToLower();}else{context.ReportError("Bad Request.");context.OperationResult.SetResultState(WellKnownContextData.HttpStatusCode, 500);}};});}}
  •  创建订阅(通过webSocket方式)
.AddInMemorySubscriptions()//.AddRedisSubscriptions((sp) => ConnectionMultiplexer.Connect("127.0.0.1:6379,password=Michael,defaultDatabase=2")).AddSubscriptionType(q => q.Name("Subscription")).AddTypeExtension<SuperheroSubscribe>()//.AddSubscriptionType<SuperheroSubscribe>()//指定一个订阅类,所有订阅写在一起[ExtendObjectType("Subscription")]public class SuperheroSubscribe{[Subscribe][Topic("SuperheroUpdated")]public async Task<SuperheroDto> SuperheroUpdated([EventMessage] SuperheroDto superherodto, [Service] ISuperheroRepository _repository){var ret = await _repository.UpdateSuperheroAsync(superherodto.Id, $"{superherodto.Name}_{DateTime.Now.ToString("yyMMdd")}");superherodto.Description = "Subscribe-SuperheroModified";return superherodto;}#region 混合模式(订阅逻辑和解析器分离)/// <summary>/// 数据逻辑处理/// </summary>/// <param name="receiver"></param>/// <returns></returns>public async IAsyncEnumerable<SuperheroDto> SubscribeToSuperheroDto([Service] ITopicEventReceiver receiver, [Service] ISuperheroRepository _repository){yield return new SuperheroDto { Id = Guid.NewGuid(), Name = $"Name-{DateTime.Now.ToString("HHmmss")}" };//return ISourceStream<SuperheroDto>var source = await receiver.SubscribeAsync<SuperheroDto>("SuperheroModified");Task.Delay(3000);await foreach (SuperheroDto superherodto in source.ReadEventsAsync()){superherodto.Name = $"{superherodto.Name}_{DateTime.Now.ToString("HHmmss")}";var ret = await _repository.UpdateSuperheroAsync(superherodto.Id, superherodto.Name);yield return superherodto;}}/// <summary>/// 订阅/// 服务端必须开启websocket/// 订阅人监听websocket/// </summary>/// <param name="superherodto"></param>/// <param name="_repository"></param>/// <returns></returns>[Topic("SuperheroModified")][Subscribe(With = nameof(SubscribeToSuperheroDto))]public async Task<SuperheroDto> SuperheroModified([EventMessage] SuperheroDto superherodto){return superherodto;}#endregion}

必须先链接WebSocket,Mutation事件才会推送 ,GraphQL语法

subscription{superheroUpdated {idnamedescription}
}

 Promgram 整体配置

 var builder = WebApplication.CreateBuilder(args);Microsoft.Extensions.Configuration.ConfigurationManager configuration = builder.Configuration;// Add services to the container.builder.Services.AddControllers();// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbucklebuilder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();builder.Services.AddGraphQLServer().AddInMemorySubscriptions()//.AddRedisSubscriptions((sp) => ConnectionMultiplexer.Connect("127.0.0.1:6379,password=Michael,defaultDatabase=2")).AddSubscriptionType(q => q.Name("Subscription")).AddTypeExtension<SuperheroSubscribe>()//.AddSubscriptionType<SuperheroSubscribe>()//指定一个订阅类,所有订阅写在一起//.AddTypeExtensionsFromFile("./Stitching.graphql")//.ModifyOptions(options =>//{//    /*//     * code-first模式-Explicit:显示绑定(手动绑定字段,或者使用ObjectType<T>-override Configure手动设置)//     * 显示绑定 必须所有数据都要声明包括 Tsortinput,Tinput,TOperationFilterInput等。。。//     * Annotation-based模式-Implicit:隐式绑定(默认展示所有字段,或者使用ObjectType<T>-override Configure手动设置)//     * GraphQLIgnoreAttribute 可过滤不需要的字段//     *///    options.DefaultBindingBehavior = BindingBehavior.Explicit;//}).AddType<SuperheroType>().AddType<MovieType>().AddType<SuperpowerType>()//.AddQueryType<MyQuery>()//一个Query,所有Query写在一起.AddQueryType(q => q.Name("Query")).AddTypeExtension<SuperheroQuery>().AddTypeExtension<MovieQuery>().AddTypeExtension<SuperpowerQuery>().AddMutationType(m => m.Name("Mutation")).AddTypeExtension<SuperheroMutation>().AddProjections().AddFiltering().AddSorting().SetPagingOptions(new PagingOptions{MaxPageSize = 10000,DefaultPageSize = 10,IncludeTotalCount = true}).AddDirectiveType<MyDirectiveType>().AddDirectiveType<ToUpperDirectiveType>().AddType<toLowerDirective>();string appRoot = builder.Environment.ContentRootPath;Environment.SetEnvironmentVariable("AppDataDirectory", System.IO.Path.Combine(appRoot, "App_Data"));var SqlServerConnStr = Environment.ExpandEnvironmentVariables(configuration.GetConnectionString("SqlServer"));// Add Application Db Context optionsbuilder.Services.AddDbContext<ApplicationDbContext>(options =>options.UseSqlServer(SqlServerConnStr));// Register custom services for the superheroesbuilder.Services.AddScoped<ISuperheroRepository, SuperheroRepository>();builder.Services.AddScoped<ISuperpowerRepository, SuperpowerRepository>();builder.Services.AddScoped<IMovieRepository, MovieRepository>();var app = builder.Build();// Configure the HTTP request pipeline.if (app.Environment.IsDevelopment()){app.UseSwagger();app.UseSwaggerUI();}app.UseHttpsRedirection();app.UseAuthorization();app.MapControllers();//使用GraphQL-Subscription 必须开启websocketapp.UseWebSockets();//https://blog.christian-schou.dk/how-to-implement-graphql-in-asp-net-core/app.MapGraphQL();app.Run();
  •  https://localhost:7199/graphql/

 

通过Strawberry Shake,自动链接GraphQL服务,创建客户端

 Introduction - Strawberry Shake - ChilliCream GraphQL Platform

 NSwagStudio,通过Swagger.json 文档创建 TypeScript Client、CSharp Client、CSharp Controller

NSwagStudio · RicoSuter/NSwag Wiki · GitHub

相关文章:

Hot Chocolate 构建 GraphQL .Net Core 服务

Hot Chocolate 是 .NET 平台下的一个开源组件库, 您可以使用它创建 GraphQL 服务, 它消除了构建成熟的 GraphQL 服务的复杂性, Hot Chocolate 可以连接任何服务或数据源&#xff0c;并创建一个有凝聚力的服务&#xff0c;为您的消费者提供统一的 API。 我会在 .NET 应用中使用…...

linux shell 入门学习笔记16 流程控制开发

shell的流程控制一般包括if、for、while、case/esac、until、break、continue语句构成。 if语句开发 单分支if //方式1 if <条件表达式> then 代码。。。 fi //方式2 if <条件表达式>;then 代码。。。 fi 双分支if if <条件表达式> then 代码1 if <条件表…...

机器学习:基于朴素贝叶斯对花瓣花萼的宽度和长度分类预测

机器学习&#xff1a;基于朴素贝叶斯对花瓣花萼的宽度和长度分类预测 作者&#xff1a;AOAIYI 作者简介&#xff1a;Python领域新星作者、多项比赛获奖者&#xff1a;AOAIYI首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞…...

给VivoBook扩容重装系统

现在笔记本重装系统都这么复杂吗&#xff1f;原谅我还是10年前的装机水平&#xff0c;折腾了一天终于把系统重新安装好了。 笔记本&#xff1a; ASUS VivoBook 安装系统&#xff1a; Win10 1、扩容 电脑配的512G硬盘满了要换个大的&#xff0c;后盖严丝合缝&#xff0c;不…...

vue 依赖注入使用教程

vue 中的依赖注入&#xff0c;官网文档已经非常详细&#xff0c;笔者在这里总结一份 目录 1、背景介绍 2、代码实现 2.1、依赖注入固定值 2.2、 依赖注入响应式数据 3、注入别名 4、注入默认值 5、应用层 Provide 6、使用 Symbol 作注入名 1、背景介绍 为什么会出现依…...

【再临数据结构】Day1. 稀疏数组

前言 这不单单是稀疏数组的开始&#xff0c;也是我重学数据结构的开始。因此&#xff0c;在开始说稀疏数组的具体内容之前&#xff0c;我想先说一下作为一个有着十余年“学龄”的学生&#xff0c;所一直沿用的一个学习方法&#xff1a;3W法。我认为&#xff0c;只有掌握了正确的…...

二十四、MongoDB 聚合运算( aggregate )

MongoDB 聚合( aggregate ) 用于处理数据&#xff0c;比如统计平均值,求和等。然后返回计算后的数据结果 MongoDB 聚合有点类似 SQL 语句中的 COUNT( * ) aggregate() 方法 MongoDB aggregate() 为 MongoDB 数据库提供了聚合运算 语法 aggregate() 方法的语法如下 > d…...

【C++】6.模板初阶

交换两个数 任何一个类型交换还要重新写一个函数 如何解决&#xff1f; 模板->写跟类型无关的函数 1.泛型编程 泛型编程&#xff1a;编写与类型无关的通用代码&#xff0c;是代码复用的一种手段。模板是泛型编程的基础。 如何写一个函数适用所有类型的交换? #include &…...

Docker部署Airbyte

Linux环境部署前置要求机器配置2c4g(最低)&#xff0c;4c8g&#xff08;推荐&#xff09;dockerdocker-compose &#xff08;要求新版本的docker-compose&#xff09;安装airbyte,打开终端&#xff0c;进入你想安装airbyte的目录。#Clone代码 git clone https://github.com/air…...

2023王道考研数据结构笔记第一章绪论

第一章 绪论 1.1 数据结构的基本概念 1.数据&#xff1a;数据是信息的载体&#xff0c;是描述客观事物属性的数、字符以及所有能输入到计算机中并被程序识别和处理的符号的集合。 2.数据元素&#xff1a;数据元素是数据的基本单位&#xff0c;通常作为一个整体进行考虑和处理…...

告别空指针让代码变优雅,Optional使用图文例子源码解读

一、前言 我们在开发中最常见的异常就是NullPointerException&#xff0c;防不胜防啊&#xff0c;相信大家肯定被坑过&#xff01; 这种基本出现在获取数据库信息中、三方接口&#xff0c;获取的对象为空&#xff0c;再去get出现&#xff01; 解决方案当然简单&#xff0c;只…...

【C++】哈希——unordered系列容器|哈希冲突|闭散列|开散列

文章目录一、unordered系列关联式容器二、哈希概念三、哈希冲突四、哈希函数五、解决哈希冲突1.闭散列——开放定址法2.代码实现3.开散列——开链法4.代码实现六、结语一、unordered系列关联式容器 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c…...

mysql-面试

锁&#xff1a; mysql的锁分为全局锁、表锁、行锁、间隙锁 全局锁&#xff1a;Flush tables with read lock 可以全局设计库为只读 表锁&#xff1a;一种是表锁&#xff0c;一种是元数据锁&#xff08;meta data lock&#xff0c;MDL&#xff09; lock tables t1 read,t2 wi…...

【夏虫语冰】Win10局域网下两台电脑无法ping通: 无法访问目标主机

文章目录1、简介2、修改高级共享设置3、启用防火墙规则4、局域网内的其他主机访问NAT模式下的虚拟机4.1 虚拟机网络设置4.2 访问测试4.2.1 http测试4.2.2 curl测试4.2.3 telnet测试4.2.4 端口占用测试5、其他结语1、简介 ping 192.168.31.134ping主机ip时&#xff0c;访问无法…...

大数据框架之Hadoop:MapReduce(三)MapReduce框架原理——Join多种应用

3.7.1Reduce Join 1、工作原理 Map端的主要工作&#xff1a;为来自不同表或文件的key/value对&#xff0c;打标签以区别不同来源的记录。然后用连接字段作为key&#xff0c;其余部分和新加的标志作为value&#xff0c;最后进行输出。 Reduce端的主要工作&#xff1a;在Reduc…...

SSRF漏洞原理、危害以及防御与修复

一、SSRF漏洞原理漏洞概述SSRF&#xff08;Server-side Request Forge&#xff0c;服务端请求伪造&#xff09;是一种由攻击者构造形成由服务端发起请求的安全漏洞。一般情况下&#xff0c;SSRF攻击的目标是从外网无法访问的内部系统。正是因为它是由服务端发起的&#xff0c;所…...

CV学习笔记-ResNet

ResNet 文章目录ResNet1. ResNet概述1.1 常见卷积神经网络1.2 ResNet提出背景2. ResNet网络结构2.1 Residual net2.2 残差神经单元2.3 Shortcut2.4 ResNet50网络结构3. 代码实现3.1 Identity Block3.2 Conv Block3.3 ResNet网络定义3.4 整体代码测试1. ResNet概述 1.1 常见卷积…...

百亿数据,毫秒级返回查询优化

近年来公司业务迅猛发展&#xff0c;数据量爆炸式增长&#xff0c;随之而来的的是海量数据查询等带来的挑战&#xff0c;我们需要数据量在十亿&#xff0c;甚至百亿级别的规模时依然能以秒级甚至毫秒级的速度返回&#xff0c;这样的话显然离不开搜索引擎的帮助&#xff0c;在搜…...

cpp之STL

STL原理 STL ⼀共提供六⼤组件&#xff0c;包括容器&#xff0c;算法&#xff0c;迭代器&#xff0c;仿函数&#xff0c;适配器和空间配置器&#xff0c;彼此可以组合套⽤。容器通过配置器取得数据存储空间&#xff0c;算法通过迭代器存取容器内容&#xff0c;仿函数可以协助算…...

基于Spring Boot开发的资产管理系统

文章目录 项目介绍主要功能截图:登录首页信息软件管理服务器管理网络设备固定资产明细硬件管理部分代码展示设计总结项目获取方式🍅 作者主页:Java韩立 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

Caliper 配置文件解析:fisco-bcos.json

config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...