C#でGraphQL（ホットチョコレートとEFCore）その３

引き続き、[ホットチョコレート](https://chillicream.com/docs/hotchocolate/v13)を調査しました。
今回はSQLServerに接続します。
N+1とかどうなるんでしょうか
※ちなみに[ホットチョコレート](https://chillicream.com/docs/hotchocolate/v13)は、Microsoft.NETベースのGraphQLサーバです。

前回の記事はこちら
[C#でGraphQLその２](https://qiita.com/htagu/items/9fe1018f97edaca9896d)

# 前回に続き、簡単でした！
結論としては
SQLServerへの接続はEFCoreの導入とホットチョコレートのプロジェクションと呼ばれる機能を使うようです。
主な変更点は以下のQueryクラスの変更とEFCoreの設定になります。
* 新規にUseProjectionアトリビュートを追加する。
* データソースをEFCoreのDbContextにする。
* あとここキモですが返値の型は"IQueryable"にしろと（このあとSQLを構築するんでしょうね）
    ```C#
    public class Query
    {
        [UseProjection]
        [UseFiltering]
        public IQueryable<Author> Authors([Service] AppDbContext dbContext)
        {
            return dbContext.Authors;
        }
        [UseProjection]
        [UseFiltering]
        public IQueryable<Book> Books([Service] AppDbContext dbContext)
        {
            return dbContext.Books;
        }
    } 
    ```
# テスト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666176/f7bb866a-75d0-4b83-9b1d-66a84b8b72d3.png)

以下が発行されたSQLですが、Joinしてくれてますねー
```SQL
Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[@__p_0='?' (Size = 256)], CommandType='Text', 
CommandTimeout='30']
SELECT [a].[Name], [a].[Id], [b].[Title], [b].[Id]
FROM [Authors] AS [a]
LEFT JOIN [Books] AS [b] ON [a].[Id] = [b].[AuthorId]
WHERE ([a].[Name] IS NOT NULL) AND ((@__p_0 LIKE N'') OR CHARINDEX(@__p_0, [a].[Name]) > 0)
ORDER BY [a].[Id]
 ```
# 疑問
ここで疑問がわきます。
AuthorテーブルとBookテーブルの関連性はどう解釈しているのでしょうか。
これについては、EFCoreの機能になるようです。
次のAppDbContextをマイグレーションすると
```C#
public class AppDbContext : DbContext
{
    public AppDbContext() { }
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    public DbSet<Author> Authors { get; set; }
    public DbSet<Book> Books { get; set; }

    public class Author
    {
        public int Id { get; internal set; }
        public string Name { get; set; }
        public List<Book> Books { get; set; }
    }
    public class Book
    {
        public int Id { get; internal set; }
        public string Title { get; set; }
        public Author Author { get; set; }
    }
}
```
次のSQLServerテーブルが生成されます。
Booksテーブルに自動的にAuthorIdとFKが追加されているのがわかります。
このId（あとFK）でリレーションを判断しているようです。（おそらく）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666176/48167e9f-4e7e-b875-91a6-a0454b6c7c50.png)

# 感想
いやあ素晴らしいですねGraphQL（ホットチョコレート）！
あとEFCoreとの相性も。
結局ここまで50行程度のプログラムで実現できました。
適切に設計されたエンティティがあれば、ほぼノーコードで
プログラムいらんやん！
仕事がなくなる．．．

# 手順まとめ

1. EFCore関連モジュール導入
    ```
    dotnet add package HotChocolate.Data.EntityFramework
    dotnet add package Microsoft.EntityFrameworkCore
    dotnet add package Microsoft.EntityFrameworkCore.Desinge
    dotnet add package Microsoft.EntityFrameworkCore.SqlServer
    ```
1. Progoram.csを以下で上書きしてください（いいかげんコード分割しなければ…） 
    ```C# 
	using Microsoft.EntityFrameworkCore;
	using static AppDbContext;

    //ASP.NET Core定義
	var builder = WebApplication.CreateBuilder(args);
	builder.Services
	    .AddDbContext<AppDbContext>(options => options.UseSqlServer("Data Source=(localdb)\\MSSQLLocaldb;Initial Catalog=GraphQL;Integrated Security=True"))
	    .AddGraphQLServer()
	    .RegisterDbContext<AppDbContext>()
	    .AddQueryType<Query>()
	    .AddProjections()
	    .AddFiltering();
	var app = builder.Build();

	app.MapGraphQL();

	app.Run();

    //GraphQL(HotChocolate)のQuery定義
	public class Query
	{
	    [UseProjection]
	    [UseFiltering]
	    public IQueryable<Author> Authors([Service] AppDbContext dbContext)
	    {
	        return dbContext.Authors;
	    }
	    [UseProjection]
	    [UseFiltering]
	    public IQueryable<Book> Books([Service] AppDbContext dbContext)
	    {
	        return dbContext.Books;
	    }
	}
 
    //EFCore定義(Context,Entity)
	public class AppDbContext : DbContext
	{
	    public AppDbContext() { }
	    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
	    public DbSet<Author> Authors { get; set; }
	    public DbSet<Book> Books { get; set; }

	    public class Author
	    {
	        public int Id { get; internal set; }
	        public string Name { get; set; }
	        public List<Book> Books { get; set; }
	    }
	    public class Book
	    {
	        public int Id { get; internal set; }
	        public string Title { get; set; }
	        public Author Author { get; set; }
	    }
	}
    ```
1.  あとマイグレーションとかデータ登録とか
※あらかじめSQLServerのDB作っておいてください
    ```
      dotnet ef migrations add CreateDB
      dotnet ef database update
    ```
    
    ```SQL
    INSERT INTO Authors VALUES(N'川端康成');
    INSERT INTO Authors VALUES(N'夏目漱石');
    INSERT INTO Books VALUES(N'雪国', 1);
    INSERT INTO Books VALUES(N'伊豆の踊子', 1);
    INSERT INTO Books VALUES(N'坊ちゃん', 2);
    INSERT INTO Books VALUES(N'三四郎', 2);
    ```