GraphQL ---02 GraphQL和C#结合的实战项目

 

本文章是介绍和记录如何创建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

1
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的结构是这样的:

  1. public class User
  2. {
  3. public int Id { get; set; }
  4.  
  5. public string Name { get; set; }
  6.  
  7. public int Age { get; set; }
  8.  
  9. public string Gender { get; set; }
  10. }

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

  1. public class UserType:ObjectGraphType<User>// 继承自ObjectGraphType,并传递范型User
  2. {
  3. public UserType()// 在构造函数中,对属性作影射
  4. {
  5. Name = "User";
  6.  
  7. Field(x => x.Id);
  8. Field(x => x.Name);
  9. Field(x => x.Age);
  10. Field(x => x.Gender);
  11. }
  12. }

  2)定义操作模型:

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

  • 定义Query操作
  1. public class Query : ObjectGraphType// 定义Query
  2. {
  3. private IWrapper wrapper = new Wrapper();
  4. IEnumerable<User> users = null;
  5. public Query()
  6. {
  7. Field<ListGraphType<UserType>>(//在构造函数中定义查询操作
  8. name: "users", //注意这个名字,后边查询的时候需要对应
  9. arguments: new QueryArguments //定义查询参数
  10. {
  11. new QueryArgument<StringGraphType>
  12. {
  13. Name = "name",
  14. Description = "The name for the user"
  15. },
  16. new QueryArgument<IntGraphType>
  17. {
  18. Name = "age",
  19. Description = "The age for the user"
  20. },
  21. new QueryArgument<StringGraphType>
  22. {
  23. Name = "gender",
  24. Description = "The gender for user"
  25. }
  26. },
  27. resolve: context =>// 定义查询操作的执行
  28. {
  29. var usercontext = context.UserContext;// 获取上下文,可在此作用户验证操作
  30. users = wrapper.User.Find(u => true);
  31. var name = context.GetArgument<string>("name");
  32. users = users.Where(u => name == null || u.Name == name);
  33. var age = context.GetArgument<int?>("age");
  34. users = users.Where(u => age == null || u.Age == age);
  35. var gender = context.GetArgument<string>("gender");
  36. users = users.Where(u => gender == null || u.Gender == gender);
  37. return users;
  38. });
        }
    }
  • 定义Mutation操作
  1. public class Mutation:ObjectGraphType
  2. {
  3. private IWrapper wrapper = new Wrapper();
  4. IEnumerable<User> users = null;
  5. public Mutation()
  6. {
  7. Field<UserType>(
  8. name: "createUser",
  9. arguments: new QueryArguments(
  10. new QueryArgument<NonNullGraphType<UserInputType>>
  11. {
  12. Name = "user"
  13. }
  14. ),
  15. resolve: context =>
  16. {
  17. var user = context.GetArgument<User>("user");
  18. return wrapper.User.Add(user);
  19. }
  20. );
  21. }
  22. }

  3. 定义GraphSchema

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

  1. public class GraphSchema:Schema
  2. {
  3. public GraphSchema()
  4. {
  5. Query = new Query();
  6. Mutation = new Mutation();
  7. }
  8. }

  4. 附.

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

  1. public class GraphQLQuery
  2. {
  3. public string OperationName { get; set; }
  4. public string NamedQuery { get; set; }
  5. public string Query { get; set; }
  6.  
  7. public object UserContext { get; set; }
  8. public JObject Variables { get; set; }
  9. }
  1. public class ActionExecute
  2. {
  3. private IDocumentExecuter executer;
  4. private IDocumentWriter writer;
  5. private ISchema schema;
  6.  
  7. public ActionExecute()
  8. {
  9. executer = new DocumentExecuter();
  10. writer = new DocumentWriter();
  11. schema = new GraphSchema();
  12. }
  13.  
  14. public async Task<ExecutionResult> ExecuteAction(GraphQLQuery query)
  15. {
  16. var result = await executer.ExecuteAsync(_ =>
  17. {
  18. _.Schema = schema;
  19. _.Query = query.Query;
  20. _.Inputs = query.Variables.ToInputs();// 查询变量的输入
  21. _.OperationName = query.OperationName;// 操作名称
  22. _.UserContext = query.UserContext;// 添加用户上下文对象
  23. _.ValidationRules = DocumentValidator.CoreRules(); // 添加自定义查询验证 逻辑
  24. _.ExposeExceptions = true;// 是否追踪错误
  25. _.FieldMiddleware.Use<ErrorHandlerMiddleware>(); // 使用中间件
  26. _.EnableMetrics = true;// 是否使用查询度量
  27.  
  28. _.ComplexityConfiguration = new ComplexityConfiguration // 防止恶意查询
  29. {
  30. MaxComplexity = 12,
  31. MaxDepth = 15 // 允许查询总最大嵌套数
  32. };
  33. });
  34. return result;
  35. }
  36.  
  37. public async Task<string> Execute(GraphQLQuery query)
  38. {
  39. var result = await ExecuteAction(query).ConfigureAwait(false);
  40.  
  41. var json = await writer.WriteToStringAsync(result);
  42.  
  43. return json;
  44. }
  45. }

四、 测试和检验

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

  1. 查询测试:
  1. public class QueryTest
  2. {
  3. private ActionExecute execute = new ActionExecute();
  4. [Fact]
  5. public void TestMethod1()
  6. {
  7. Assert.True(1 == 1);
  8. }
  9. [Theory]
  10. [InlineData(16, "Male")]
  11. [InlineData(18, "FeMale")]
  12. public async void QueryUsers(int age, string gender)
  13. {
  14. var queryStr = @"{users(age:" + age + ",gender:" + "\"" + gender + "\"" + "){id name gender age}}";
  15. var result = await execute.ExecuteAction(new GraphQLQuery { Query = queryStr,UserContext= "Add Role" });
  16. var data = result.Data;
  17. Assert.Null(result.Errors?.Count);
  18. }
  19. }

  为了检验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.变更测试:

  1. public class MutationTest
  2. {
  3. private ActionExecute execute = new ActionExecute();
  4.  
  5. [Theory]
  6. [InlineData(16, "Test1")]
  7. [InlineData(18, "Test2")]
  8. public async void CreateUser(int age, string name)
  9. {
  10. var queryStr = @"{query: mutation ($user: UserInput!){createUser(user:$user){id name age}},variables:{user:{name: " + name + @",age:" + age + @"}}}";
  11.  
  12. var query = new GraphQLQuery
  13. {
  14. Query = "mutation ($user: UserInput!){createUser(user:$user){id name age}}",
  15. Variables = JObject.Parse("{user:{\"name\": \"" + name + "\",\"age\":" + age + "}}")
  16. };
  17. var result = await execute.ExecuteAction(query);
  18. Assert.Null(result.Errors.Count);
  19. }
  20. }

  发现了什么?

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

五、后续

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

GraphQL和C#的更多相关文章

  1. Facebook的Web开发三板斧:React.js、Relay和GraphQL

    2015-02-26 孙镜涛  InfoQ Eric Florenzano最近在自己的博客上发表了一篇题为<Facebook教我们如何构建网站>的文章,他认为软件开发有些时候需要比较大的跨 ...

  2. facebook graphql

    思想先进,前端直接从后台调用所需要的数据. 最简单的理解: 从"select * from 学生表" 进化为"select name, sex from 学生表" ...

  3. Graphql介绍(Introduction to GraphQL)

    Introduction to GraphQL  GraphQL介绍 Learn about GraphQL, how it works, and how to use it in this seri ...

  4. graphql 新API 开发方式

    我们知道 GraphQL 使用 Schema 来描述数据,并通过制定和实现 GraphQL 规范 定义了支持 Schema 查询的 DSQL (Domain Specific Query Langua ...

  5. [GraphQL] Use GraphQLNonNull for Required Fields

    While certain fields in a GraphQL Schema can be optional, there are some fields or arguments that ar ...

  6. [GraphQL] Use Arguments in a GraphQL Query

    In GraphQL, every field and nested object is able to take in arguments of varying types in order to ...

  7. [GraphQL] Write a GraphQL Schema in JavaScript

    Writing out a GraphQL Schema in the common GraphQL Language can work for simple GraphQL Schemas, but ...

  8. [GraphQL] Serve a GraphQL Schema as Middleware in Express

    If we have a GraphQL Schema expressed in terms of JavaScript, then we have a convenient package avai ...

  9. [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, ...

  10. [GraphQL] Use GraphQL's Object Type for Basic Types

    We can create the most basic components of our GraphQL Schema using GraphQL's Object Types. These ty ...

随机推荐

  1. java倒计时使用ScheduledExecutor实现,使用两个线程,以秒为单位

    public class Countdown2 { private volatile int lin; private int curSec; public Countdown2(int lin) t ...

  2. 4.3 if-else语句使用

    Q:对输入的成绩进行登记划分. #include<iostream> #include<cstdio> using namespace std; int main() { in ...

  3. UVa 10859 - Placing Lampposts 树形DP 难度: 2

    题目 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&a ...

  4. 获取音、视频时长(NAudio,Shell32,FFmpeg)

    参考网址:https://blog.csdn.net/u013810234/article/details/57471780 以下为本次测试用到的音.视频格式: audio :”.wav;.mp3;. ...

  5. Java的Properties类使用

    一.Java Properties类 Java中有个比较重要的类Properties(Java.util.Properties),主要用于读取Java的配置文件,各种语言都有自己所支持的配置文件,配置 ...

  6. 异常处理机制中的return关键字

    Java中,执行try-catch-finally语句需要注意: 第一:return语句并不是函数的最终出口,如果有finally语句,这在return之后还会执行finally(return的值会暂 ...

  7. hustoj 管理员和后台设置

    之前用过hustoj 的livecd版本,觉得有一些小问题,所以从头到尾搭建.主要包含的过程包括: 安装ubuntu系统 搭建hustoj 管理员和后台资源建设 本文介绍如何在搭建好hustoj的基础 ...

  8. בוא--来吧--IPA--希伯来语

    灰常好听的希伯来语歌曲, Rita唱得真好.

  9. 4.4 C++虚析构函数

    参考:http://www.weixueyuan.net/view/6373.html 总结: 构造函数是不能声明为虚函数的,析构函数可以被声明为虚函数. 将基类的析构函数声明为虚函数之后,派生类的析 ...

  10. FFT理解

     *连续时间-周期性信号频谱 clc;clear;close all N = input('N= '); T = 0.05; n = 1:N; %原始数据输入 D = 2*pi/(N*T); %计算分 ...