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 可以连接任何服务或数据源,并创建一个有凝聚力的服务,为您的消费者提供统一的 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 <条件表…...

机器学习:基于朴素贝叶斯对花瓣花萼的宽度和长度分类预测
机器学习:基于朴素贝叶斯对花瓣花萼的宽度和长度分类预测 作者:AOAIYI 作者简介:Python领域新星作者、多项比赛获奖者:AOAIYI首页 😊😊😊如果觉得文章不错或能帮助到你学习,可以点赞…...
给VivoBook扩容重装系统
现在笔记本重装系统都这么复杂吗?原谅我还是10年前的装机水平,折腾了一天终于把系统重新安装好了。 笔记本: ASUS VivoBook 安装系统: Win10 1、扩容 电脑配的512G硬盘满了要换个大的,后盖严丝合缝,不…...

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

【再临数据结构】Day1. 稀疏数组
前言 这不单单是稀疏数组的开始,也是我重学数据结构的开始。因此,在开始说稀疏数组的具体内容之前,我想先说一下作为一个有着十余年“学龄”的学生,所一直沿用的一个学习方法:3W法。我认为,只有掌握了正确的…...
二十四、MongoDB 聚合运算( aggregate )
MongoDB 聚合( aggregate ) 用于处理数据,比如统计平均值,求和等。然后返回计算后的数据结果 MongoDB 聚合有点类似 SQL 语句中的 COUNT( * ) aggregate() 方法 MongoDB aggregate() 为 MongoDB 数据库提供了聚合运算 语法 aggregate() 方法的语法如下 > d…...
【C++】6.模板初阶
交换两个数 任何一个类型交换还要重新写一个函数 如何解决? 模板->写跟类型无关的函数 1.泛型编程 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。 如何写一个函数适用所有类型的交换? #include &…...
Docker部署Airbyte
Linux环境部署前置要求机器配置2c4g(最低),4c8g(推荐)dockerdocker-compose (要求新版本的docker-compose)安装airbyte,打开终端,进入你想安装airbyte的目录。#Clone代码 git clone https://github.com/air…...

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

告别空指针让代码变优雅,Optional使用图文例子源码解读
一、前言 我们在开发中最常见的异常就是NullPointerException,防不胜防啊,相信大家肯定被坑过! 这种基本出现在获取数据库信息中、三方接口,获取的对象为空,再去get出现! 解决方案当然简单,只…...

【C++】哈希——unordered系列容器|哈希冲突|闭散列|开散列
文章目录一、unordered系列关联式容器二、哈希概念三、哈希冲突四、哈希函数五、解决哈希冲突1.闭散列——开放定址法2.代码实现3.开散列——开链法4.代码实现六、结语一、unordered系列关联式容器 在C98中,STL提供了底层为红黑树结构的一系列关联式容器,…...
mysql-面试
锁: mysql的锁分为全局锁、表锁、行锁、间隙锁 全局锁:Flush tables with read lock 可以全局设计库为只读 表锁:一种是表锁,一种是元数据锁(meta data lock,MDL) 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时,访问无法…...

大数据框架之Hadoop:MapReduce(三)MapReduce框架原理——Join多种应用
3.7.1Reduce Join 1、工作原理 Map端的主要工作:为来自不同表或文件的key/value对,打标签以区别不同来源的记录。然后用连接字段作为key,其余部分和新加的标志作为value,最后进行输出。 Reduce端的主要工作:在Reduc…...

SSRF漏洞原理、危害以及防御与修复
一、SSRF漏洞原理漏洞概述SSRF(Server-side Request Forge,服务端请求伪造)是一种由攻击者构造形成由服务端发起请求的安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。正是因为它是由服务端发起的,所…...

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 常见卷积…...

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

cpp之STL
STL原理 STL ⼀共提供六⼤组件,包括容器,算法,迭代器,仿函数,适配器和空间配置器,彼此可以组合套⽤。容器通过配置器取得数据存储空间,算法通过迭代器存取容器内容,仿函数可以协助算…...
基于Spring Boot开发的资产管理系统
文章目录 项目介绍主要功能截图:登录首页信息软件管理服务器管理网络设备固定资产明细硬件管理部分代码展示设计总结项目获取方式🍅 作者主页:Java韩立 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...