Slide 1

Slide 1 text

.NET MagicOnion Mayuki Sawatari

Slide 2

Slide 2 text

About Me Mayuki Sawatari @mayuki http://github.com/mayuki Working at Cysharp, Inc. MagicOnion Maintainer Microsoft MVP for Developer Technologies

Slide 3

Slide 3 text

[PR]WeekRef.NET .NET https://WeekRef.NET/

Slide 4

Slide 4 text

MagicOnion

Slide 5

Slide 5 text

MagicOnion .NET RPC Cysharp MIT https://github.com/Cysharp/MagicOnion

Slide 6

Slide 6 text

MagicOnion .NET .NET/C# (IDL) 1 /1 RPC ORM

Slide 7

Slide 7 text

MagicOnion .NET .NET/C# (IDL) 1 /1 RPC

Slide 8

Slide 8 text

.NET/C# IDL IDL Client Server (IDL) API

Slide 9

Slide 9 text

.NET/C# IDL / .NET C# IDL C# ? (MagicOnion ) C# interface C# Client C# Server C# interface .NET .NET interface API interface

Slide 10

Slide 10 text

.NET/C# IDL API C# C# interface namespace ApiService1; public interface IGreeterService { string SayHello(string name); } IGreeterService SayHello string string

Slide 11

Slide 11 text

.NET/C# IDL C# // var client = Client.Create(); var result = client.SayHello("Alice"); Console.WriteLine(result); // => Hello Alice // public class GreeterService : IGreeterService { public string SayHello(string name) => $"Hello {name}"; } // public interface IGreeterService { string SayHello(string name); }

Slide 12

Slide 12 text

.NET/C# IDL MagicOnion // var client = MagicOnionClient.Create(channel); var result = await client.SayHelloAsync("Alice"); Console.WriteLine(result); // => Hello Alice // public class GreeterService : ServiceBase, IGreeterService { public async UnaryResult SayHelloAsync(string name) => $"Hello {name}"; } // public interface IGreeterService : IService { UnaryResult SayHelloAsync(string name); }

Slide 13

Slide 13 text

.NET/C# IDL C#/.NET interface .NET

Slide 14

Slide 14 text

=

Slide 15

Slide 15 text

Client Server API API API Client Server API API : 1:1

Slide 16

Slide 16 text

MagicOnion / Server Client Client Client リクエスト レスポンス Server Client Client Client 通知 通知 リクエスト API

Slide 17

Slide 17 text

MagicOnion .NET .NET/C# (IDL) 1 /1 RPC

Slide 18

Slide 18 text

SignalR MagicOnion SignalR (= ) MagicOnion .NET (.NET, Unity) (= ) Strongly-typed MagicOnion .NET interface object

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

.NET 8+ .NET Standard 2.x .NET 8+ Unity (Windows, iOS, Android) .NET C#

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

.NET Runtime ASP.NET Core Grpc.AspNetCore.Server MagicOnion.Server OS (Windows, Linux, macOS) OS (Windows, Linux, macOS, iOS, Android) .NET Runtime Unity SocketsHttpHandler YetAnotherHttpHandler HttpClient Grpc.Net.Client MagicOnion.Client gRPC HTTP/2 MessagePack TCP/IP TLS UDS/Named Pipe

Slide 25

Slide 25 text

ASP.NET Core gRPC gRPC over HTTP/2 gRPC MessagePack HttpClient C# gRPC

Slide 26

Slide 26 text

Demo: API

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

IDE IDE .NET

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Tips Visual Studio 2022 17.11 Multiple Startup Projects https://learn.microsoft.com/en-us/visualstudio/ide/how-to-set-multiple-startup-projects?view=vs-2022

Slide 31

Slide 31 text

MagicOnion

Slide 32

Slide 32 text

MagicOnion Unary StreamingHub

Slide 33

Slide 33 text

Unary

Slide 34

Slide 34 text

Unary 1 1 HTTP API Client Server API API : 1:1

Slide 35

Slide 35 text

Unary: IService Task/Task UnaryResult/UnaryResult MagicOnion Protocol Buffer public interface IGreeterService : IService { UnaryResult SayHelloAsync(string name); } UnaryResult Task/ValueTask

Slide 36

Slide 36 text

Unary: ServiceBase ASP.NET Core MVC Web API Controller public class GreeterService : ServiceBase, IGreeterService { public async UnaryResult SayHelloAsync(string name) => $"Hello {name}"; }

Slide 37

Slide 37 text

Unary: MagicOnionClient.Create // var channel = GrpcChannel.ForAddress("https://localhost:7127"); var client = MagicOnionClient.Create(channel); var result = await client.SayHelloAsync("Alice"); Console.WriteLine(result); // => Hello Alice

Slide 38

Slide 38 text

Unary: Web → MagicOnion ( ) public class MyFilterAttribute : MagicOnionFilterAttribute { public override async ValueTask Invoke(ServiceContext context, Func next) { // var request = context.GetRawRequest(); // … // await next(context); // var response = context.GetRawResponse(); // … context.SetRawResponse(response); // … } }

Slide 39

Slide 39 text

Unary: ASP .NET Core HTTP RateLimiting Authorize RateLimitingMiddleware (ASP.NET Core) AuthorizeMiddleware (ASP.NET Core) MetricsFilter MyFilter SayHelloAsync

Slide 40

Slide 40 text

StreamingHub

Slide 41

Slide 41 text

StreamingHub Client Server API

Slide 42

Slide 42 text

Demo: StreamingHub OpenAI

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

StreamingHub: IStreamingHub ValueTask/ValueTask ( ) // public interface IGreeterHub : IStreamingHub { ValueTask HelloAsync(string name); } // ( ) public interface IGreeterHubReceiver { void OnMessageReceived(string message); } void

Slide 45

Slide 45 text

StreamingHub: StreamingHubBase Unary Controller SignalR Hub Client public class GreeterHub : StreamingHubBase, IGreeterHub { public async ValueTask HelloAsync(string name) { // _ = Task.Run(async () => { await Task.Delay(1000); Client.OnMessageReceived($" {name}"); }); return $"Hello {name}"; } } Client.OnMessageReceived vNext

Slide 46

Slide 46 text

StreamingHub: StreamingHubClient.ConnectAsync & var channel = GrpcChannel.ForAddress("https://localhost:7127"); var receiver = new GreeterHubReceiver(); var hub = await StreamingHubClient.ConnectAsync(channel, receiver); var result = await hub.HelloAsync("Alice"); Console.WriteLine(result); // => Hello Alice // 1 " Alice" ( ) class GreeterHubReceiver : IGreeterHubReceiver { public void OnMessageReceived(string message) => Console.WriteLine(message); }

Slide 47

Slide 47 text

StreamingHub 1 1Hub Hub Client Hub Client Hub Client Hub Client …

Slide 48

Slide 48 text

StreamingHub Hub Hub Hub Client Hub Client Hub Client Hub Client …

Slide 49

Slide 49 text

StreamingHub: … Hub Client Hub Client Hub Client Hub Client Group: Room-B Group: Room-A

Slide 50

Slide 50 text

StreamingHub: (JoinAsync) (SendMessageAsync) OnMessage public interface IChatHub : IStreamingHub { ValueTask JoinAsync(string roomName, string name); ValueTask SendMessageAsync(string message); } public interface IChatHubReceiver { void OnMessage(string name, string message); }

Slide 51

Slide 51 text

StreamingHub: v6.x public class ChatHub : StreamingHubBase, IChatHub { private IGroup? _group; private string? _name; public async ValueTask JoinAsync(string roomName, string name) { // roomName _group = await Group.AddAsync(roomName); _name = name; } public ValueTask SendMessageAsync(string message) { // OnMessage Broadcast(_group).OnMessage(_name, message); return default; } }

Slide 52

Slide 52 text

StreamingHub: v7 public class ChatHub : StreamingHubBase, IChatHub { private IGroup? _group; private string? _name; public async ValueTask JoinAsync(string roomName, string name) { // roomName _group = await Group.AddAsync(roomName); _name = name; } public ValueTask SendMessageAsync(string message) { // OnMessage _group.All.OnMessage(_name, message); return default; } } ( All) vNext

Slide 53

Slide 53 text

Demo: StreamingHub public class BroadcastHub(ILogger logger) : StreamingHubBase, IBroadcastHub { private IGroup? _group; protected override async ValueTask OnConnected() { logger.LogInformation($"Client: {ConnectionId} Connected. (User-Agent: {Context.CallContext.GetHttpContext().Request.Headers.UserAgent})"); _group = await Group.AddAsync("All"); } public async ValueTask CompleteAsync(string message) { logger.LogInformation($"Begin CompleteAsync: Input={message}"); var client = new OpenAIClient("").AsChatClient(modelId: "gpt-4o-mini"); var stream = client.CompleteStreamingAsync([new ChatMessage(ChatRole.User, message)]); Broadcast(_group).OnCompleteStarted(message); await foreach (var response in stream) { Broadcast(_group).OnMessageReceived(response.Text); } Broadcast(_group).OnCompleteFinished(); logger.LogInformation($"Finished CompleteAsync"); } }

Slide 54

Slide 54 text

StreamingHub: SignalR vNext

Slide 55

Slide 55 text

.NET

Slide 56

Slide 56 text

Metrics / .NET Aspire .NET 8 Metrics API MeterSource Aspire Dashboard

Slide 57

Slide 57 text

Source Generator / Native AOT Native AOT/IL2CPP MagicOnion Source Generator Native AOT Unity IL2CPP

Slide 58

Slide 58 text

Source Generator / Native AOT MagicOnion.Client Native AOT Native AOT

Slide 59

Slide 59 text

Blazor WASM + gRPC-Web gRPC-Web Blazor WebAssembly Grpc.Net.Client.Web, Grpc.AspNet.Core.Web Unary StreamingHub grpc-web

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

.NET RPC C# API Unary StraemingHub API C# API .NET ASP .NET Core ASP.NET Core grpc-dotnet Source Generator Metrics API

Slide 62

Slide 62 text

MagicOnion - C# .NET Core/Unity | Cygames Engineers' Blog https://tech.cygames.co.jp/archives/3181/ NextGen Server/Client Architecture - gRPC + Unity + C# https://www.slideshare.net/slideshow/nextgen-serverclient-architecture-grpc-unity-c/72604810