{"id":16428507,"url":"https://github.com/buraksenyurt/dotnet7-services","last_synced_at":"2025-03-23T07:33:32.211Z","repository":{"id":94515576,"uuid":"596988803","full_name":"buraksenyurt/dotnet7-services","owner":"buraksenyurt","description":"In this repo, I include the codes that I try to examine the service development strategies on the .Net 7 side through a sample solution.","archived":false,"fork":false,"pushed_at":"2023-12-04T09:08:54.000Z","size":5330,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-10-12T08:17:17.932Z","etag":null,"topics":["api","azure-functions","dotnet","dotnet-core","dotnet7","entity-framework-core","grpc","jwt","postgresql","rest-api","rust-lang","services","signalr","web-api"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/buraksenyurt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-02-03T11:29:02.000Z","updated_at":"2023-10-05T11:28:57.000Z","dependencies_parsed_at":"2023-07-28T07:15:46.623Z","dependency_job_id":null,"html_url":"https://github.com/buraksenyurt/dotnet7-services","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buraksenyurt%2Fdotnet7-services","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buraksenyurt%2Fdotnet7-services/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buraksenyurt%2Fdotnet7-services/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buraksenyurt%2Fdotnet7-services/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/buraksenyurt","download_url":"https://codeload.github.com/buraksenyurt/dotnet7-services/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221847081,"owners_count":16890961,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","azure-functions","dotnet","dotnet-core","dotnet7","entity-framework-core","grpc","jwt","postgresql","rest-api","rust-lang","services","signalr","web-api"],"created_at":"2024-10-11T08:17:15.679Z","updated_at":"2024-10-28T15:13:37.694Z","avatar_url":"https://github.com/buraksenyurt.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# .Net 7 App Services\n\nIn this repo, I include the codes that I try to examine the service development strategies on the .Net 7 side through a sample solution. Solution uses a simple and popular database, chinook. Database provider is Postgresql and reside on docker container.\n\n## Some Useful Informations\n\n```shell\n# to use Postgresql\ndocker run --name postgresql -e POSTGRES_DB=chinook POSTGRES_USER=scoth -e POSTGRES_PASSWORD=tiger -p 5432:5432 -v /data:/var/lib/postgresql/data -d postgres\n\n# to use ef tool\ndotnet tool install -g dotnet-ef\n# or update the existing dotnet-ef tool\ndotnet tool update -g dotnet-ef\n\n# create model classes from Chinook Db\n# execute on RockShop.Common.Models project library\ndotnet ef dbcontext scaffold \"Host=localhost;Database=chinook;Username=scoth;Password=tiger\" Npgsql.EntityFrameworkCore.Postgresql --namespace RockShop.Shared --data-annotations\n\n# start existing container\ndocker start postgresql\n# to working container list\ndocker ps -a\n# to stop container\ndocker stop postgresql\n# drop container\ndocker rm [container_id]\n\n# if you want you can use docker-compose file\ndocker-compose up -d\n```\n\n## Run _(RockShop.WebApi)_\n\n```bash\n# to run\ndotnet run\n\n# to run with https profile\ndotnet run --launch-profile https\n```\n\n### Sample Requests _(RockShop.WebApi)_\n\n```bash\n# Ping - Pong\ncurl -X 'GET' 'http://localhost:5221/ping' -H 'accept: text/plain'\n\n# Get albums with paging\ncurl -X 'GET' 'http://localhost:5221/api/albums?page=5' -H 'accept: application/json'\n\n# Get albums by artist name\ncurl -X 'GET' 'http://localhost:5221/api/albums/mfö' -H 'accept: application/json'\n\n# Get artists with paging (You have to use Bearer Token for this request)\ncurl -X 'GET' 'http://localhost:5221/api/artists?page=10' -H 'accept: application/json'\n\n# Get album by id\ncurl -X 'GET' 'http://localhost:5221/api/albums/45' -H 'accept: application/json'\n\n# Get customers which living in Portugal\ncurl -X 'GET' 'http://localhost:5221/api/customers/Portugal' -H 'accept: application/json'\n\n# Top * total sales by country\ncurl -X 'GET' 'http://localhost:5221/api/invoices/totalsales/top/5' -H 'accept: application/json'\ncurl -X 'GET' 'http://localhost:5221/api/invoices/totalsales/top/3' -H 'accept: application/json'\ncurl -X 'GET' 'http://localhost:5221/api/invoices/totalsales/top/-1' -H 'accept: application/json'\n\n# Get Some Trucks informations with paging\ncurl -X 'GET' 'http://localhost:5221/api/tracks?page=25' -H 'accept: application/json'\n\n# Add artist with albums\ncurl -X 'POST' \\\n  'http://localhost:5221/api/artists' \\\n  -H 'accept: application/json' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"name\": \"MFÖ\",\n  \"albums\": [\n    {\n      \"title\": \"Ele Gün Karşı Yapayalnız\"\n    },\n\n    {\n      \"title\": \"Peki peki anladık\"\n    },\n    {\n      \"title\": \"No Problem\"\n    },\n    {\n      \"title\": \"Vak The Rock\"\n    },\n    {\n      \"title\": \"Geldiler\"\n    },\n    {\n      \"title\": \"M.V.A.B.\"\n    }\n  ]\n}'\n\n# Delete album with id\ncurl -X 'DELETE' 'http://localhost:5221/api/albums/1234' -H 'accept: */*'\n\n# Update artist name\ncurl -X 'PUT' \\\n  'http://localhost:5221/api/artists/284' \\\n  -H 'accept: */*' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n  \"name\": \"Mazhar Fuat Özkan\"\n}'\n```\n\nAlso we can use [Postman collection](Chinook%20Rest%20Service%20[Net%207].postman_collection.json) to test service endpoints.\n\n### CORS Clients _(RockShop.WebApi)_\n\nUse following steps to test CORS with clients.\n\n- First of all start the **RockShop.WebApi.Service** project.\n- Start **RockShop.WebApi.Client.MVC** project secondly.\n- Then go to **http://localhost:5233 address** and search for some artists with Get Albums button.\n- Also go to **http://localhost:5233/home/albums** address.\n\nIn all cases please look at browser debugger(by press F12)\n\n### Rate Limiting Test _(With 3rd Party Package)_ _(RockShop.WebApi)_\n\nYou can use RockShop.WebApi.Client.Console project to test Rate Limitings. _(For any client id with decorate random guid, the rate limits is 2 request per 6 seconds)_ .For specific client test on client app use **\"terminal-client-development\"** value _(Just one call in 10 seconds)_ . In this case different rate limit policies applies which defined in appSettings for client app. Also we can use **\"development-team\"** for client identity. In this case rate limits does not apply to the client app.\n\n**case 1 _(Client Id = Random)_**\n\n![assets/rate_limit_01.png](assets/rate_limit_01.png)\n\n**case 2 _(Client Id = development-team)_**\n\n![assets/rate_limit_02.png](assets/rate_limit_02.png)\n\n**case 3 _(Client Id = terminal-client-development)_**\n\n![assets/rate_limit_03.png](assets/rate_limit_03.png)\n\n### JWT Bearer Authorization _(RockShop.WebApi)_\n\nThe JWT Bearer token-based authorization system has also been added to the service application. Symbolically implemented only for Endpoint, which pulls Tracks information. Under normal circumstances, we need to prove ourselves to the system with an identity mechanism with a user name, password, fingerprint, etc. (Authentication). However, we can create a permanent token for developments in the local development environment with the user-jwts tool.\n\n```bash\n# In the RockShop.WebApi.Service project folder\ndotnet user-jwts create\n\n# Running this command generates a token. Token details can be seen using the ID information.\ndotnet user-jwts print e5a9d922 --show-all\n```\n\n![assets/jwt_01.png](assets/jwt_01.png)\n\n![assets/jwt_02.png](assets/jwt_02.png)\n\nIn addition, the Authentication tab is added to the appsettings.development.json file related to the generated token. This content contains valid authorization information for the Bearer token. Of course for testing purposes.\n\nWe get HTTP 401 Unauthorized Error for the following request.\n\n```bash\ncurl -X 'GET' 'http://localhost:5221/api/tracks?page=1' -H 'accept: application/json'\n```\n\n![assets/jwt_03.png](assets/jwt_03.png)\n\nFor this reason we have to use Bearer Tokek on request.\n\n![assets/jwt_04.png](assets/jwt_04.png)\n\n## OData Sample\n\nTo test OData service we can use following commands.\n\n```bash\n# To get metadata\ncurl -X 'GET' 'http://localhost:5120/jukebox/$metadata' -H 'accept: application/json;odata.metadata=minimal;odata.streaming=true'\n\n# Get album by id\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/Albums/1' -H 'accept: */*'\n\n# Get all albums\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/Albums' -H 'accept: */*'\n\n# Get total count of albums\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/Albums/$count' -H 'accept: */*'\n\n# Get all artists\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/Artists' -H 'accept: */*'\n\n# Get total count of artists\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/Artists/$count' -H 'accept: */*'\n\n# Get specicif artist by id\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/Artists(16)' -H 'accept: */*'\n\n# After version usage added\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/Tracks/12' -H 'accept: */*'\ncurl -X 'GET' 'http://localhost:5120/jukebox/v2/Tracks/12' -H 'accept: */*'\n\n# OData services are usually built on querying resources. \n# However, it also allows operations such as Insert, Update, Delete.\n# Sample Insert request\ncurl -X 'POST' \\\n  'http://localhost:5120/jukebox/v1/Artists' \\\n  -H 'accept: */*' \\\n  -H 'Content-Type: application/json;odata.metadata=minimal;odata.streaming=true' \\\n  -d '{\n  \"name\": \"Mazhar Fuat Özkan\"\n}'\n# Sample Delete request for artist\ncurl -X 'DELETE' 'http://localhost:5120/jukebox/v1/Artists/287' -H 'accept: */*'\n```\n\n### OData Standard Query Materials\n\nQuery options; $select, $expand, $filter, $orderby, $skip, $top _([Grouping and Aggregation Support](https://learn.microsoft.com/en-us/odata/client/grouping-and-aggregation))_\nQuery parameters; eq, ne, lt, gt, le, ge, and, or, not, add, sub, mul, div, mod\nQuery functions; startswith, endswith, concat, contains, indexof, length, substring, tolower, toupper, trim, now, day, month, year, hour, minute, second\n\nSome usages;\n\n```bash\n# Top 10 Album titles\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/albums/?$select=title\u0026top=10' -H 'accept: */*'\n\n# Top 10 Album titles but descending order\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/albums/?$orderby=title%20desc\u0026$select=title\u0026top=10' -H 'accept: */*'\n\n# Albums which title starts with letter D\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/albums/?$filter=startswith(Title,%27D%27)\u0026select=AlbumId,Title' -H 'accept: */*'\n\n# EF runs the following query for the above OData query\n# SELECT a.\"AlbumId\", a.\"Title\"\n#      FROM \"Album\" AS a\n#      WHERE @__TypedProperty_0 = '' OR ((a.\"Title\" LIKE @__TypedProperty_0_1 || '%' ESCAPE '') \n#      AND left(a.\"Title\", length(@__TypedProperty_0))::character varying(160) = @__TypedProperty_0::character varying(160))\n\n\n# Returns a list of songs whose size is below a certain value.\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/tracks/?$filter=Bytes%20lt%201024000\u0026$select=TrackId,Name,AlbumId,Bytes' -H 'accept: */*'\n\n# Average Unit Price of All Tracks\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/Tracks?$apply=aggregate(UnitPrice%20with%20average%20as%20AverageUnitPrice)' -H 'accept: */*'\n\n# Total Invoices group by Billing Country\ncurl -X 'GET' 'http://localhost:5120/jukebox/v1/Invoices?$apply=groupby((BillingCountry),aggregate(Total%20with%20sum%20as%20TotalAmount))' -H 'accept: */*'\n```\n\n### OData MVC Client Test\n\nStart RockShop.OData.Service projcet first and than start the RockShop.OData.Client.Mvc project.\n\n![assets/odata_client_01.png](assets/odata_client_01.png)\n\n## GraphQL Sample\n\nAfter the **dotnet run** command go to http://localhost:5034/graphql...\n\n![assets/graphql_01.png](assets/graphql_01.png)\n\n![assets/graphql_02.png](assets/graphql_02.png)\n\nSample Hello World graphql queries.\n\n```json\nquery AreYouThere {\n  ping\n}\n\nquery GiveMeALuckNumber {\n  luckyNum\n}\n\nquery GetArtists {\n  artists(page:5) {\n    name\n  }\n}\n\nquery GetQueries {\n  artists(page:5) {\n    name\n  },\n  album(albumId:1){\n    title\n  }\n}\n\nquery Sales {\n  totalSalesByCountry(count: 3) {\n    country\n    total\n  }\n}\n\nquery Queries {\n  tracks(page: 10){\n    album,\n    name,\n    composer\n  }\n}\n```\n\n![assets/graphql_03.png](assets/graphql_03.png)\n\n![assets/graphql_04.png](assets/graphql_04.png)\n\n![assets/graphql_05.png](assets/graphql_05.png)\n\n![assets/graphql_06.png](assets/graphql_06.png)\n\n![assets/graphql_07.png](assets/graphql_07.png)\n\n### MVC Client Test for GraphQL\n\nStart RockShop.GraphQL projcet first and than start the RockShop.GraphQL.Client.Mvc project.\n\n![assets/graphql_08.png](assets/graphql_08.png)\n\n### Console Client for GraphQL\n\nIn this sample we use Strawberry Shake tool which creating graphql client easly.\n\n```bash\n# in console application folder\ndotnet new tool-manifest\ndotnet tool install StrawberryShake.Tools --local\n\n# After added required packages start the GraphQL server and than\n# This command adds a GraphQL client to Console application. Look at the files which has a .graphql extensions.\ndotnet graphql init http://localhost:5034/graphql -n RockShopClient\n```\n\n![assets/graphql_09.png](assets/graphql_09.png)\n\n### GraphQL Mutations\n\nSample insert message.\n\n```json\nmutation AddNewArtist {\n  addArtist(input: { name: \"Aretha Franklin\" }) {\n    artist {\n      artistId\n      name\n    }\n  }\n}\n\n// For Check (Album ID different probably)\nquery FindArtist{\n  artist(artistId: 287){\n    name\n  }\n}\n```\n\n![assets/graphql_10.png](assets/graphql_10.png)\n\n## gRPC Service Sample\n\nThis example includes a gRPC-based service and thin client. Run RockShop.Grpc.Service first, then RockShop.Grpc.Client.Mvc.\n\n![assets/grpc_01.png](assets/grpc_01.png)\n\n### Deadline Suppor for Higher Reliability\n\nOne of the gRPC techniques that can be used to increase the reliability of the service side is Deadline enforcement. The runtime results for this situation are as follows.\n\n![assets/grpc_02.png](assets/grpc_02.png)\n\n### JSON Support for non-HTTP2\n\nIn order to work with the outputs of gRPC services in JSON format on non-HTTP2 clients, the http.proto and annotations.proto files added to the project are copied from the [following address](https://github.com/dotnet/aspnetcore/tree/main/src/Grpc/JsonTranscoding/test/testassets/Sandbox/google/api).\n\nThe application RockShop.Grpc.Service.Json can be used for testing. An example call like /v1/jukebox/albums/6 can be made from the browser after the application is started.\n\n![assets/grpc_03.png](assets/grpc_03.png)\n\n### Swagger Support for JSON based service\n\nAfter adding Swagger support to the RockShop.Grpc.Service.Json application, the helper interface can be easily accessed via the swagger/index.html call.\n\n![assets/grpc_04.png](assets/grpc_04.png)\n\n### Server Reflection Support on gRPC Service\n\nAfter adding Swagger support to the RockShop.Grpc.Service application, we can use Postman to call GetArtists function as follows.\n\n![assets/grpc_05.png](assets/grpc_05.png)\n\n## SignalR App\n\nThe application named RockShop.SignalR.App is an MVC project and demonstrates the use of SignalR. An auxiliary Javascript package is used on the client side of the project. We install this package with the Library Manager CLI tool. We can follow the steps below.\n\n```bash\ndotnet tool install -g Microsoft.Web.LibraryManager.CLi\n# for check\ndotnet tool list -g\n# or\nlibman --version\n\n# In project folder use the following command\n# This loads the required js libraries for communication with signalr hub\nlibman install @microsoft/signalr@latest -p unpkg -d wwwroot/js/signalr --files dist/browser/signalr.js --files dist/browser/signalr.min.js\n```\n\nTo test the example, run the following command and go to \"https://localhost:7044\". For local tests, come to the same page from different tabs and log in with different names.\n\n```bash\ndotnet run --launch-profile https\n```\n\n![assets/signalr_01.png](assets/signalr_01.png)\n\nThe next example is an application of the Console type. The sample application only communicates with the Hub and listens to the message transmitted to it. Below is a sample runtime image.\n\n![assets/signalr_02.png](assets/signalr_02.png)\n\n### Streaming Support on SignalR Apps\n\nThe runtime output of the streaming example is as follows.\n\n![assets/signalr_03.png](assets/signalr_03.png)\n\n## Developing Azure Functions\n\nAn example Azure function. [Core Tools](https://www.npmjs.com/package/azure-functions-core-tools) may be needed to do local development. In the example of Azure functions, the open source Azurite tool is used for local tests. This tool can be installed on the system with npm.\n\n```bash\nwget -q https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb\nsudo dpkg -i packages-microsoft-prod.deb\n\nsudo apt-get update\nsudo apt-get install azure-functions-core-tools-4\n\n# if installation succesfull this works\nfunc --version\n\nsudo npm install -g azurite\n\n# To create Azure Function Project from command prompt\n# If we want to develop in In-Process mode remove --worker-runtime dotnet-isolated\nfunc init --worker-runtime dotnet-isolated --target-framework net7.0\n\n# to create a new Azure Functions function using HTTP Trigger (Authentication anonymous - No API Key Required)\nfunc new --name TopFiveBookFunction --template \"HTTP trigger\" --authlevel \"anonymous\"\n\n# to start function\nfunc start\n```\n\n![assets/azure_func_01.png](assets/azure_func_01.png)\n\n### Calling Another Service\n\nThe Azure function in the next example uses another service written in the rust programming language. The dependency injection mechanism is used in the Azure function. To test the example, the rust application must be started first.\n\n```bash\n# in app_radar folder\ncargo run\n\n# in RockShop.Azure.Service.Isolated folder\nfunc start\n```\n\n![assets/azure_func_02.png](assets/azure_func_02.png)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fburaksenyurt%2Fdotnet7-services","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fburaksenyurt%2Fdotnet7-services","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fburaksenyurt%2Fdotnet7-services/lists"}