GraphQL ---02 GraphQL和C#結合的實戰項目

来源:https://www.cnblogs.com/kingkangstudy/archive/2019/03/14/10471751.html
-Advertisement-
Play Games

本文章是介紹和記錄如何創建GraphQL項目,以及如何使用GraphQL進行數據的相關操作。項目參照GraphQL .Net 的官方文檔進行實踐 一、項目結構: 為了更好的和原有的項目結合在一起,儘可能減少對原項目的修改。我對項目結構做瞭如下分層。 二、項目結構分層說明 Contracts層: 項目 ...


本文章是介紹和記錄如何創建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 = 12,
                    MaxDepth = 15 // 允許查詢總最大嵌套數
                };
            });
            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(1 == 1);
        }
        [Theory]
        [InlineData(16, "Male")]
        [InlineData(18, "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(16, "Test1")]
        [InlineData(18, "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

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ifrem上傳文件後顯示 1、上傳文件按鈕 <a class="btn btn-primary pull-right" id="data-upload" style="margin-right:10px;" data-target="#UploadFiles" data-toggle="modal" ...
  • // string data = "{\"uid\":515,\"timestamp\":\"2018 - 5 - 25 19:05:00\",\"encode\":\"3A19E9D6EAFF2FA9160ED6981EB522E1\"}"; /// <summary> /// 與適當性認證相關的 ...
  • --一本錯誤的記錄 insert into Book values('錯誤時怎樣練成的',111) --左連接 select s.name,b.name from student as s left join Book as b on s.id=b.studentid --右連接 select s. ...
  • 一般而言webservice是部署在哪台伺服器,然後它的address location就是指向哪個,但是由於有些情況處於各種原因,如網路策略,需要先訪問某個ip之後再進行跳轉到一個ip,這個時候就需要代碼控制websevice指向的IP地址了,就需要用到SoapExtensionReflector ...
  • 裝箱:值類型-->引用類型。 拆箱:引用類型-->值類型 裝箱:把值類型拷貝一份到堆里。反之拆箱。 具有父子關係 是拆裝箱的條件之一。 所以: 裝的什麼類型,拆的時候也必須是對應類型。 使用方法時看是否裝拆箱:看需要的參數和傳入的參數的集體類型。 使用泛型集合避免裝拆箱。 ...
  • MVC頁面擴展方法 單例模式 /// <summary> /// 創建一個Config內容對象 /// </summary> /// <param name=""></param> /// <param name="key"></param> /// <returns></returns> publ ...
  • 今天給大家帶來的討論主題是通過實戰經驗來對百億數據量下的多表數據查詢進行優化,俗話說的好,一切脫離業務的架構都是耍流氓,接下來我就整理一下今天早上微信群里石頭哥給大家分享的百億數據量多表查詢架構以及優化思路。由於本文內容整理自微信群,爬樓不易,整理更不易,如果有遺漏,歡迎大家在評論區留言。 作者:依 ...
  • 之前在隨筆《在Winform開發中使用Grid++報表》介紹了在Winform環境中使用Grid++報表控制項,本篇隨筆介紹在Boostrap開發框架中使用Grid++報表,也就是Web環境中使用Grid++報表,對於我上篇提到的二維碼條形碼的資產信息表,我系統通過Web方式進行呈現,或者展示其報表的... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...