引き続き、ホットチョコレートを調査しました。
今回はSQLServerに接続します。
N+1とかどうなるんでしょうか
※ちなみにホットチョコレートは、Microsoft.NETベースのGraphQLサーバです。
前回の記事はこちら
C#でGraphQLその2
前回に続き、簡単でした!
結論としては
SQLServerへの接続はEFCoreの導入とホットチョコレートのプロジェクションと呼ばれる機能を使うようです。
主な変更点は以下のQueryクラスの変更とEFCoreの設定になります。
- 新規にUseProjectionアトリビュートを追加する。
- データソースをEFCoreのDbContextにする。
- あとここキモですが返値の型は"IQueryable"にしろと(このあとSQLを構築するんでしょうね)
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; } }
テスト
以下が発行されたSQLですが、Joinしてくれてますねー
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をマイグレーションすると
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)でリレーションを判断しているようです。(おそらく)
感想
いやあ素晴らしいですねGraphQL(ホットチョコレート)!
あとEFCoreとの相性も。
結局ここまで50行程度のプログラムで実現できました。
適切に設計されたエンティティがあれば、ほぼノーコードで
プログラムいらんやん!
仕事がなくなる...
手順まとめ
-
EFCore関連モジュール導入
dotnet add package HotChocolate.Data.EntityFramework dotnet add package Microsoft.EntityFrameworkCore dotnet add package Microsoft.EntityFrameworkCore.Desinge dotnet add package Microsoft.EntityFrameworkCore.SqlServer
-
Progoram.csを以下で上書きしてください(いいかげんコード分割しなければ…)
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; } } }
-
あとマイグレーションとかデータ登録とか
※あらかじめSQLServerのDB作っておいてくださいdotnet ef migrations add CreateDB dotnet ef database update
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);