From 94710da46d2edf4ee16fe4ab135df8790776ca7b Mon Sep 17 00:00:00 2001 From: hiromasa ohbayashi Date: Sat, 24 Oct 2020 18:10:24 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=E3=81=AA=E3=82=93=E3=81=A8=E3=81=AA?= =?UTF-8?q?=E3=81=8F=E5=8B=95=E3=81=8F=E3=81=A8=E3=81=93=E3=82=8D=E3=81=BE?= =?UTF-8?q?=E3=81=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 25 ++++++ Backend.csproj | 23 ------ Backend/Backend.csproj | 24 ++++++ Backend/Models/DbConnection.cs | 22 +++-- Backend/Models/Entities/TableSample.cs | 6 +- Backend/Models/GetTableSample.cs | 64 ++++++++++----- Backend/Program.cs | 25 ++++++ Backend/Protos/SayHello.proto | 15 ++++ Backend/Protos/TableSample.proto | 2 +- Backend/Services/SayHelloServiceImpl.cs | 28 +++++++ ...bleSample.cs => TableSampleServiceImpl.cs} | 13 ++- Backend/Startup.cs | 37 +++++++++ Backend/appsettings.Development.json | 10 +++ ClientStub/ClientStub.csproj | 28 +++++++ ClientStub/Program.cs | 81 +++++++++++++++++++ README.md | 29 +++++++ docker-compose.yml | 24 ++++++ docker/db/init/table_samples.sql | 10 +++ 18 files changed, 410 insertions(+), 56 deletions(-) create mode 100644 .gitignore delete mode 100644 Backend.csproj create mode 100644 Backend/Backend.csproj create mode 100644 Backend/Program.cs create mode 100644 Backend/Protos/SayHello.proto create mode 100644 Backend/Services/SayHelloServiceImpl.cs rename Backend/Services/{TableSample.cs => TableSampleServiceImpl.cs} (64%) create mode 100644 Backend/Startup.cs create mode 100644 Backend/appsettings.Development.json create mode 100644 ClientStub/ClientStub.csproj create mode 100644 ClientStub/Program.cs create mode 100644 docker-compose.yml create mode 100644 docker/db/init/table_samples.sql diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb80a33 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ diff --git a/Backend.csproj b/Backend.csproj deleted file mode 100644 index dea518a..0000000 --- a/Backend.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - netcoresapp3.1 - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - diff --git a/Backend/Backend.csproj b/Backend/Backend.csproj new file mode 100644 index 0000000..2856734 --- /dev/null +++ b/Backend/Backend.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp3.1 + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/Backend/Models/DbConnection.cs b/Backend/Models/DbConnection.cs index 6166d86..85c6a9b 100644 --- a/Backend/Models/DbConnection.cs +++ b/Backend/Models/DbConnection.cs @@ -1,15 +1,27 @@ using Dapper; using System; -using System.Data.SqlClient; +using System.Text.RegularExpressions; namespace Backend.Models { public class DbConnection { + // public static string connectionString = + // @"Data Source=localhost\tablespace_sample;" + // +"Initial Catalog=base;" + // +"User Id=admin;" + // +"Password=adminsample;"; public static string connectionString = - @"Data Source=localhost\tablespace_sample;" - +"Initial Catalog=base;" - +"User Id=admin;" - +"Password=adminsample;"; + @"Server=localhost;" + +"Port=5432;" + +"Database=sampledb;" + +"User ID=postgres;" + +"Password=password0123password;" + +"Enlist=true;"; + + public static string TrimSql(string sql, int spaceLength) + { + return Regex.Replace(sql, $"\n {{{spaceLength}}}", "\n"); + } } } diff --git a/Backend/Models/Entities/TableSample.cs b/Backend/Models/Entities/TableSample.cs index 34c0d35..fb280c8 100644 --- a/Backend/Models/Entities/TableSample.cs +++ b/Backend/Models/Entities/TableSample.cs @@ -1,12 +1,8 @@ -using Dapper; using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; namespace Backend.Models.Entities { + // [DataContract(Name = "table_samples")] public class TableSample { public int Id { get; set; } diff --git a/Backend/Models/GetTableSample.cs b/Backend/Models/GetTableSample.cs index 1efc964..b566ff7 100644 --- a/Backend/Models/GetTableSample.cs +++ b/Backend/Models/GetTableSample.cs @@ -1,10 +1,10 @@ using Dapper; +using Npgsql; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; -using System.Data.SqlClient; using Protobuf; namespace Backend.Models @@ -19,17 +19,17 @@ public GetTableSample(KeysRequest request) Status = -1; Present = null; - ver (acceptStatus, accptResult) = this.accept(request); - if (acceptStatus < 0) + var (acceptStatus, accptResult) = this.accept(request); + if (acceptStatus <= 0) { Status = acceptStatus; return; } - ver (presentStatus, presentResult) = this.present(accptResult); - if (presentResult < 0) + var (presentStatus, presentResult) = this.present(accptResult); + if (presentStatus < 0) { - Status = presentResult; + Status = presentStatus; return; } @@ -39,41 +39,69 @@ public GetTableSample(KeysRequest request) private (int, Entities.TableSample) accept(KeysRequest request) { - Console.WriteLine($"request.id = {request.id}"); + // Console.WriteLine($"request.id = {request.Id}"); var sql = $@" select id, code, name, - created_at, + created_at from table_samples where id = @requestId ; "; - Console.WriteLine($"query = {sql}"); - Console.WriteLine($"query = {Regex.Replace(sql, "^ ", String.Empty)}"); + // Console.WriteLine($"query = {DbConnection.TrimSql(sql, 6)}"); try { Entities.TableSample row; - using (SqlConnection conn = new SqlConnection()) + // using (SqlConnection conn = new SqlConnection()) + using (NpgsqlConnection conn = new NpgsqlConnection(DbConnection.connectionString)) { - conn.ConnectionString = DbConnection.connectionString; + // conn.ConnectionString = DbConnection.connectionString; conn.Open(); - Console.WriteLine($"db.State = {conn.State}"); - row = conn.QuerySingleOrDefault(sql, new { requestId = request.Id }); + Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; + // Console.WriteLine($"db.State = {conn.State}"); + // var tests = conn.QuerySingleOrDefault("select * from tests where id = @requestId", new { requestId = 1 }); + // Console.WriteLine($"tests.name = {tests.Name}"); + row = conn.QuerySingleOrDefault(DbConnection.TrimSql(sql, 6), new { requestId = request.Id }); + Console.WriteLine(row); + if (row == null) + { + Console.WriteLine("result 0."); + return (0, null); + } + else + { + Console.WriteLine($"row.id = {row.Id}"); + Console.WriteLine($"row.code = {row.Code}"); + Console.WriteLine($"row.name = {row.Name}"); + return (1, row); + } } - Console.WriteLine($"row.id = {row.Id}"); - Console.WriteLine($"row.code = {row.Code}"); - Console.WriteLine($"row.name = {row.Name}"); - return (1, row); } catch { return (-1, null); } } + + private (int, Protobuf.TableSample) present(Entities.TableSample queryResult) + { + var row = new TableSample(); + try + { + row.Id = queryResult.Id; + row.Code = queryResult.Code; + row.Name = queryResult.Name; + } + catch + { + return (-1, null); + } + return (1, row); + } } } diff --git a/Backend/Program.cs b/Backend/Program.cs new file mode 100644 index 0000000..55c640a --- /dev/null +++ b/Backend/Program.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace Backend +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/Backend/Protos/SayHello.proto b/Backend/Protos/SayHello.proto new file mode 100644 index 0000000..5e58b6a --- /dev/null +++ b/Backend/Protos/SayHello.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +option csharp_namespace = "Protobuf"; +package Services; + +service SayHelloService { + rpc SayHello (HelloRequest) returns (HelloReply); +} + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string message = 1; +} diff --git a/Backend/Protos/TableSample.proto b/Backend/Protos/TableSample.proto index 0f4ddca..7775bf3 100644 --- a/Backend/Protos/TableSample.proto +++ b/Backend/Protos/TableSample.proto @@ -4,7 +4,7 @@ package Services; import "google/protobuf/timestamp.proto"; -service TableSample { +service TableSampleService { rpc GetAllTableSamples (FiltersRequest) returns (TableSamples); rpc GetTableSample (KeysRequest) returns (TableSample); rpc InsertTableSample (TableSample) returns (Empty); diff --git a/Backend/Services/SayHelloServiceImpl.cs b/Backend/Services/SayHelloServiceImpl.cs new file mode 100644 index 0000000..85ad839 --- /dev/null +++ b/Backend/Services/SayHelloServiceImpl.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Grpc.Core; +using Microsoft.Extensions.Logging; +using Protobuf; + +namespace Backend +{ + public class SayHelloServiceImpl : SayHelloService.SayHelloServiceBase + { + private readonly ILogger _logger; + + public SayHelloServiceImpl(ILogger logger) + { + _logger = logger; + } + + public override Task SayHello(HelloRequest request, ServerCallContext context) + { + return Task.FromResult(new HelloReply + { + Message = "Hello " + request.Name + }); + } + } +} diff --git a/Backend/Services/TableSample.cs b/Backend/Services/TableSampleServiceImpl.cs similarity index 64% rename from Backend/Services/TableSample.cs rename to Backend/Services/TableSampleServiceImpl.cs index 6199bf4..5098ee5 100644 --- a/Backend/Services/TableSample.cs +++ b/Backend/Services/TableSampleServiceImpl.cs @@ -8,19 +8,24 @@ namespace Backend { - public class TableSample : Protobuf.TableSample.TableSampleBase + public class TableSampleServiceImpl : TableSampleService.TableSampleServiceBase { - private readonly ILogger _logger; + private readonly ILogger _logger; + + public TableSampleServiceImpl(ILogger logger) + { + _logger = logger; + } // public override Task GetAllTableSamples(FiltersRequest requestData, ServerCallContext context) // { // var responseData = new TableSamples(); // } - public override Task GetTableSample(KeysRequest requestData, ServerCallContext context) + public override Task GetTableSample(KeysRequest request, ServerCallContext context) { var tableSample = new Models.GetTableSample(request); - if (tableSample < 0) + if (tableSample.Status <= 0) { return null; } diff --git a/Backend/Startup.cs b/Backend/Startup.cs new file mode 100644 index 0000000..d8351b9 --- /dev/null +++ b/Backend/Startup.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Protobuf; + +namespace Backend +{ + #region snippet + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddGrpc(); + } + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapGrpcService(); + endpoints.MapGrpcService(); + }); + } + } + #endregion +} diff --git a/Backend/appsettings.Development.json b/Backend/appsettings.Development.json new file mode 100644 index 0000000..47200ab --- /dev/null +++ b/Backend/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Grpc": "information", + "Microsoft": "Information" + } + } +} diff --git a/ClientStub/ClientStub.csproj b/ClientStub/ClientStub.csproj new file mode 100644 index 0000000..901c4ac --- /dev/null +++ b/ClientStub/ClientStub.csproj @@ -0,0 +1,28 @@ + + + + Exe + netcoreapp3.1 + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + Protos\SayHello.proto + Protos\TableSample.proto + + + + diff --git a/ClientStub/Program.cs b/ClientStub/Program.cs new file mode 100644 index 0000000..d46c780 --- /dev/null +++ b/ClientStub/Program.cs @@ -0,0 +1,81 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Protobuf; +using Grpc.Core; +using Grpc.Net.Client; + +namespace ClientStub +{ + class Program + { + static async Task Main(string[] args) + { + // The port number(5001) must match the port of the gRPC server. + using var channel = GrpcChannel.ForAddress("https://localhost:5001"); + using (Task task = TestSayHello(channel)) + { + Console.WriteLine(task.Result); + } + using (Task task = TestGetTableSample(channel, 2)) + { + Console.WriteLine(task.Result); + } + using (Task task = TestGetTableSample(channel, 3)) + { + Console.WriteLine(task.Result); + } + Console.WriteLine("Press any key to exit..."); + Console.ReadKey(); + } + + static async Task TestSayHello(GrpcChannel channel) + { + var client = new SayHelloService.SayHelloServiceClient(channel); + try + { + var reply = await client.SayHelloAsync( + new HelloRequest { Name = "Hello!" }); + return $"reply.Message = {reply.Message}"; + } + catch (RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded) + { + return "SayHello timeout."; + } + catch + { + return "SayHello faild."; + } + } + + static async Task TestGetTableSample(GrpcChannel channel, int id) + { + var client = new TableSampleService.TableSampleServiceClient(channel); + try + { + TableSample res = + await client.GetTableSampleAsync( + new KeysRequest { Id = id }); + if (res == null) + { + return "result 0."; + } + else + { + return + @$"TableSample\n" + +$" .code = {res.Code}\n" + +$" .name = {res.Name}"; + } + } + catch (RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded) + { + return "GetTableSample timeout."; + } + catch + { + return "GetTableSample faild."; + } + } + } +} diff --git a/README.md b/README.md index 2d627b3..e789fe8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,34 @@ # backend-csharp 1. For grpc-based templates. +## Prerequisites - 前提条件 +1. OS: Windows 10 pro +1. Lang: C# +1. Framework: .Net Core 3.1 + 1. Download - +1. Docker - [docker-compose.yml](docker-compose.yml) + 1. DB: PostgresSQL + 1. Viewer: adminer +1. csharp project NuGet + 1. See "*.csproj" for the version. + 1. [Backend.csproj](Backend/Backend.csproj) + 1. Google.Protobuf + 1. Related to gRPC + 1. Dapper + 1. Npgsql + 1. [ClientStub.csproj](ClientStub/ClientStub.csproj) + +### Tested environment - 動作確認した環境 +1. Last check: 2020-10-22 +1. tools + 1. VSCode: v1.50.1 + 1. extend + 1. Microsoft + 1. Docker: v1.7.0 + 1. C#: v1.23.3 + 1. Other + 1. vscode-proto3: v0.5.2 + ## Add in the following tutorials 1. [チュートリアル: ASP.NET Core で gRPC のクライアントとサーバーを作成する](https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-3.1&tabs=visual-studio-code) +1. [Dapper Tutorial](https://dapper-tutorial.net/dapper) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..0279c2b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3.1' + +services: + db: + # https://hub.docker.com/_/postgres + image: postgres:13-alpine + container_name: container-name + ports: + - 5432:5432 + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password0123password + - PGPASSWORD=password0123password + - POSTGRES_DB=sampledb + - DATABASE_HOST=localhost + volumes: + - ./docker/db/data + # - ./docker/db/init:/docker-entrypoint-initdb.d + + adminer: + image: adminer + restart: always + ports: + - 8080:8080 diff --git a/docker/db/init/table_samples.sql b/docker/db/init/table_samples.sql new file mode 100644 index 0000000..863eb60 --- /dev/null +++ b/docker/db/init/table_samples.sql @@ -0,0 +1,10 @@ +create table table_samples ( + "id" serial not null, + "code" text not null, + "name" text null, + "created_at" timestamp not null +); + +insert into table_sample values + (1, 'code1', 'NameOne', clock_timestamp()), + (2, 'code2', 'NameTwo', clock_timestamp()); From 9a0da8437190c8f7c5195856098037bbfe09cb59 Mon Sep 17 00:00:00 2001 From: hiromasa ohbayashi Date: Sun, 8 Nov 2020 15:54:40 +0900 Subject: [PATCH 2/2] add Operation Procedure --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index e789fe8..858d954 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,40 @@ 1. Other 1. vscode-proto3: v0.5.2 +## Operation Procedure - 動作手順 +### preparation - 準備 +1. git clone + ```powershell + git clone https://github.com/flatring/backend-csharp.git + cd backend-csharp + ``` +1. docker run + ```posershell + docker-compose up -d + ``` +1. insert data > Adminer + 1. brower + `localhost:8080` + 1. Adminer login + 1. See [docker-compose.yml](docker-compose.yml) + 1. execute sql + 1. sql command + 1. `docker\db\init\table_samples.sql` + +### :one: - server +1. power shell 起動 + ```powershell + cd backend-csharp/Backend + dotnet run + ``` + +### :two: - client +1. power shell 起動 + ```powershell + cd backend-csharp/ClientStub + dotnet run + ``` + ## Add in the following tutorials 1. [チュートリアル: ASP.NET Core で gRPC のクライアントとサーバーを作成する](https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-3.1&tabs=visual-studio-code) 1. [Dapper Tutorial](https://dapper-tutorial.net/dapper)