本文章是介绍和记录如何创建GraphQL项目,以及如何使用GraphQL进行数据的相关操作。项目参照GraphQL .Net 的官方文档进行实践

一、项目结构:

  为了更好的和原有的项目结合在一起,尽可能减少对原项目的修改。我对项目结构做了如下分层。

二、项目结构分层说明

  Contracts层: 项目的接口层,重点存放项目的一些接口。和原项目的分层结构的Contracts一致

  Entities层: 实体模型层,存放实体模型。与原有项目的分层结构Entites层一致

  GraphQLDemo: 是使用Console控制台应用程序对GraphQL的调用实例

  GraphQLs: 使用GraphQL 的模型定义和查询、变更等操作的定义

  Services: 提供服务的具体实现层,和原有项目分层中的Services 层一致

  Tests: 使用Unit Test 测试调用GraphQL

在这里重点关注 标红的部分的介绍

三、GraphQLs项目介绍:

  GraphQLs重点是存储项目的GraphQL操作相关的内容

  1.在项目解决方案中,新建程序集,命名为GraphQLs

  2. 安装Graphql

NuGet 搜索 GraphQL

  3.创建GraphQL 的相关概念

  GraphQL有两种方式创建Schema,

    • 一种是使用Schema First,也就是使用GraphQL Schema Language创建Schema. 可以对比EntityFramework的DB First
    • 一种是使用Graph Type定义Schema,可以对比EntityFramework 的Code First

  在这里适用Code First定义数据模型,可以与原有的数据服务应用一起使用。可分为以下步骤:

  1)定义数据模型:

  假设原有的数据模型Book的结构是这样的:

    public class User
{
public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public string Gender { get; set; }
}

  那么定义对应的GraphQL的数据模型可以是这样的:

    public class UserType:ObjectGraphType<User>// 继承自ObjectGraphType,并传递范型User
{
public UserType()// 在构造函数中,对属性作影射
{
Name = "User"; Field(x => x.Id);
Field(x => x.Name);
Field(x => x.Age);
Field(x => x.Gender);
}
}

  2)定义操作模型:

  GraphQL的操作分为: Query(Select), Mutation(Create,Update,Delete),Subscription(订阅)

  • 定义Query操作
    public class Query : ObjectGraphType// 定义Query
{
private IWrapper wrapper = new Wrapper();
IEnumerable<User> users = null;
public Query()
{
Field<ListGraphType<UserType>>(//在构造函数中定义查询操作
name: "users", //注意这个名字,后边查询的时候需要对应
arguments: new QueryArguments //定义查询参数
{
new QueryArgument<StringGraphType>
{
Name = "name",
Description = "The name for the user"
},
new QueryArgument<IntGraphType>
{
Name = "age",
Description = "The age for the user"
},
new QueryArgument<StringGraphType>
{
Name = "gender",
Description = "The gender for user"
}
},
resolve: context =>// 定义查询操作的执行
{
var usercontext = context.UserContext;// 获取上下文,可在此作用户验证操作
users = wrapper.User.Find(u => true);
var name = context.GetArgument<string>("name");
users = users.Where(u => name == null || u.Name == name);
var age = context.GetArgument<int?>("age");
users = users.Where(u => age == null || u.Age == age);
var gender = context.GetArgument<string>("gender");
users = users.Where(u => gender == null || u.Gender == gender);
return users;
});
    }
}
  • 定义Mutation操作
    public class Mutation:ObjectGraphType
{
private IWrapper wrapper = new Wrapper();
IEnumerable<User> users = null;
public Mutation()
{
Field<UserType>(
name: "createUser",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<UserInputType>>
{
Name = "user"
}
),
resolve: context =>
{
var user = context.GetArgument<User>("user");
return wrapper.User.Add(user);
}
);
}
}

  3. 定义GraphSchema

  定义GraphSchema就是定义Schema的Query、Mutation、Subscription操作

    public class GraphSchema:Schema
{
public GraphSchema()
{
Query = new Query();
Mutation = new Mutation();
}
}

  4. 附.

  为了检验查询、修改操作,这里定义一个GraphQLQuery来定义操作,并定义一个查询操作类

    public class GraphQLQuery
{
public string OperationName { get; set; }
public string NamedQuery { get; set; }
public string Query { get; set; } public object UserContext { get; set; }
public JObject Variables { get; set; }
}
 public class ActionExecute
{
private IDocumentExecuter executer;
private IDocumentWriter writer;
private ISchema schema; public ActionExecute()
{
executer = new DocumentExecuter();
writer = new DocumentWriter();
schema = new GraphSchema();
} public async Task<ExecutionResult> ExecuteAction(GraphQLQuery query)
{
var result = await executer.ExecuteAsync(_ =>
{
_.Schema = schema;
_.Query = query.Query;
_.Inputs = query.Variables.ToInputs();// 查询变量的输入
_.OperationName = query.OperationName;// 操作名称
_.UserContext = query.UserContext;// 添加用户上下文对象
_.ValidationRules = DocumentValidator.CoreRules(); // 添加自定义查询验证 逻辑
_.ExposeExceptions = true;// 是否追踪错误
_.FieldMiddleware.Use<ErrorHandlerMiddleware>(); // 使用中间件
_.EnableMetrics = true;// 是否使用查询度量 _.ComplexityConfiguration = new ComplexityConfiguration // 防止恶意查询
{
MaxComplexity = ,
MaxDepth = // 允许查询总最大嵌套数
};
});
return result;
} public async Task<string> Execute(GraphQLQuery query)
{
var result = await ExecuteAction(query).ConfigureAwait(false); var json = await writer.WriteToStringAsync(result); return json;
}
}

四、 测试和检验

  一切准备就绪,下边对创建的GraphQL进行测试

  1. 查询测试:
    public class QueryTest
{
private ActionExecute execute = new ActionExecute();
[Fact]
public void TestMethod1()
{
Assert.True( == );
}
[Theory]
[InlineData(, "Male")]
[InlineData(, "FeMale")]
public async void QueryUsers(int age, string gender)
{
var queryStr = @"{users(age:" + age + ",gender:" + "\"" + gender + "\"" + "){id name gender age}}";
var result = await execute.ExecuteAction(new GraphQLQuery { Query = queryStr,UserContext= "Add Role" });
var data = result.Data;
Assert.Null(result.Errors?.Count);
}
}

  为了检验GraphQL的查询优越性,你可以修改一下queryStr=@"{users{id name gender age}}"; 或queryStr=@"{users{gender age}}";queryStr=@"{users{ name age}}";注意这里的@和{}只是C# 对字符串操作的一种方式。

  发现了什么?

  如果我们在前端(Web、微信小程序、手机APP),在web端,作为后台管理系统,我可能需要获取用户的所有信息,那么我可能需要使用queryStr=@"{users{id name gender age}}"。在微信小程序端,我只要根据用户的id查询用户名字就可以了,那么我只用变动查询语句:queryStr=@"{users(id){ name}}";

  意味着什么?

  意味着我们只需要提供一个API接口,该端口接受传递的查询字符串就可以了。所有的实体都可以只用这一个接口了。想查询什么,由前端决定了,再也不需要追着后端接口开发工程师要数据了。我想这样以来,前端和后端只需要一个接口沟通,会比REST API来的更方便了。

2.变更测试:

    public class MutationTest
{
private ActionExecute execute = new ActionExecute(); [Theory]
[InlineData(, "Test1")]
[InlineData(, "Test2")]
public async void CreateUser(int age, string name)
{
var queryStr = @"{query: mutation ($user: UserInput!){createUser(user:$user){id name age}},variables:{user:{name: " + name + @",age:" + age + @"}}}"; var query = new GraphQLQuery
{
Query = "mutation ($user: UserInput!){createUser(user:$user){id name age}}",
Variables = JObject.Parse("{user:{\"name\": \"" + name + "\",\"age\":" + age + "}}")
};
var result = await execute.ExecuteAction(query);
Assert.Null(result.Errors.Count);
}
}

  发现了什么?

  同样的。我们只需要传递查询的参数,传递对应的参数Variables 就能完成修改动作。同时,该变更和查询的操作字符串语句很像,只是多了一个mutation。

五、后续

  这篇文章只是介绍了使用控制台和UnitTest测试使用了GraphQL,后续会更新在Asp.Net Core MVC 中使用GraphQL,也可以学习杨旭的文章。很好的博主https://www.cnblogs.com/cgzl/p/9691323.html

可参考项目:https://github.com/JinGangRed/graphql-management 和https://github.com/JinGangRed/graphql_learning

GraphQL ---02 GraphQL和C#结合的实战项目的更多相关文章

  1. [GraphQL] Use GraphQL's List Type for Collections

    In order to handle collections of items in a GraphQL Schema, GraphQL has a List Type. In this video, ...

  2. Android快乐贪吃蛇游戏实战项目开发教程-01项目概述与目录

    一.项目简介 贪吃蛇是一个很经典的游戏,也很适合用来学习.本教程将和大家一起做一个Android版的贪吃蛇游戏. 我已经将做好的案例上传到了应用宝,无病毒.无广告,大家可以放心下载下来把玩一下.应用宝 ...

  3. Linux系统实战项目——sudo日志审计

    Linux系统实战项目——sudo日志审计   由于企业内部权限管理启用了sudo权限管理,但是还是有一定的风险因素,毕竟运维.开发等各个人员技术水平.操作习惯都不相同,也会因一时失误造成误操作,从而 ...

  4. .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现

    本篇我将带着大家一起来对Dapper进行下封装并实现基本的增删改查.分页操作的同步异步方法的实现(已实现MSSQL,MySql,PgSQL).同时我们再实现一下仓储层的代码生成器,这样的话,我们只需要 ...

  5. 11. SpringCloud实战项目-初始化数据库和表

    SpringCloud实战项目全套学习教程连载中 PassJava 学习教程 简介 PassJava-Learning项目是PassJava(佳必过)项目的学习教程.对架构.业务.技术要点进行讲解. ...

  6. 19.SpringCloud实战项目-SpringCloud整合Alibaba-Nacos配置中心

    SpringCloud实战项目全套学习教程连载中 PassJava 学习教程 简介 PassJava-Learning项目是PassJava(佳必过)项目的学习教程.对架构.业务.技术要点进行讲解. ...

  7. 21.SpringCloud实战项目-后台题目类型功能(网关、跨域、路由问题一文搞定)

    SpringCloud实战项目全套学习教程连载中 PassJava 学习教程 简介 PassJava-Learning项目是PassJava(佳必过)项目的学习教程.对架构.业务.技术要点进行讲解. ...

  8. Python实战项目网络爬虫 之 爬取小说吧小说正文

    本次实战项目适合,有一定Python语法知识的小白学员.本人也是根据一些网上的资料,自己摸索编写的内容.有不明白的童鞋,欢迎提问. 目的:爬取百度小说吧中的原创小说<猎奇师>部分小说内容 ...

  9. android经典实战项目视频教程下载

    注:这是一篇转载的文章,原文具体链接地址找不到了,将原文分享如下,希望能对看到的朋友有所帮助! 最近在学习android应用方面的技术,自己在网上搜集了一些实战项目的资料,感觉挺好的,发布出来跟大伙分 ...

随机推荐

  1. Nuget 多平台多目标快速自动打包

    构建现代的 .Net 应用离不开 Nuget 的支持,而快速打包 Nuget 成了提高生产率的有效方法. 1. 前置条件 为了实现 Nuget 的快速打包,我们需要先解决一些前置依赖,无论是 .Net ...

  2. 安装php的lavavel框架的流程(亲测)

    windows上安装配置laravel 初次接触laravel会觉得它的安装方式比较不一样,与Linux上安装软件的方式很相似. 学习laravel的最好方式当然是看官方文档,笔者就在这里讲官方文档上 ...

  3. 上板子在线抓波发现app_rdy一直为低

    现象 使用Xilinx的MIG IP测试外挂DDR3的读写发现一段很短的时间后app_rdy恒为低,并且最后一个读出的数据全是F. (1)不读写数据,app_rdy正常为高,MIG IP初始化信号为高 ...

  4. mybatis-generator自动生成代码插件

    mybatis自动生成代码(实体类.Dao接口等)是很成熟的了,就是使用mybatis-generator插件. 它是一个开源的插件,使用maven构建最好,可以很方便的执行 插件官方简介: http ...

  5. 【CF1152F】Neko Rules the Catniverse(动态规划)

    [CF1152F]Neko Rules the Catniverse(动态规划) 题面 CF 题解 我们先考虑一个需要扫一遍所有位置的做法. 那么状态一定是\(f[i]\)然后什么什么表示考虑到当前第 ...

  6. POJ 3666 Making the Grade (动态规划)

    Description A straight dirt road connects two fields on FJ's farm, but it changes elevation more tha ...

  7. 关于snmp octet string和普通string问题

    我是获取的Octet String用String输出,输出的是一连串的2个16进制数 空格.:然后想对输出结果操作,得到我想要的值. 解决方案:private static string exchan ...

  8. [Reinforcement Learning] 马尔可夫决策过程

    在介绍马尔可夫决策过程之前,我们先介绍下情节性任务和连续性任务以及马尔可夫性. 情节性任务 vs. 连续任务 情节性任务(Episodic Tasks),所有的任务可以被可以分解成一系列情节,可以看作 ...

  9. Python——爬取人口迁徙数据(以腾讯迁徙为例)

    说明: 1.迁徙量是腾讯修改后的数值,无法确认真实性. 2.代码运行期间,腾讯迁徙未设置IP屏蔽和浏览器检测,因此下段代码仅能保证发布近期有效. 3.代码功能:爬取指定一天的四十(此四十是根据自己的城 ...

  10. redis---------AOF文件异常导致的redis无法载入

    AOF损坏时的对策1.若在写AOF文件时Server崩溃则可能导致AOF文件损坏而不能被Redis载入.可通过如下步骤修复: 创建一个AOF文件的备份: cp appendonly.aof appen ...