{"id":14976037,"url":"https://github.com/antoniofalcaojr/dotnet6.graphql4.webapplication","last_synced_at":"2025-10-27T18:30:17.285Z","repository":{"id":37866970,"uuid":"282532283","full_name":"AntonioFalcaoJr/Dotnet6.GraphQL4.WebApplication","owner":"AntonioFalcaoJr","description":"This project exemplifies the implementation and dockerization of a simple Razor Web MVC Core consuming a full GraphQL 4 Web API, build in a .NET 6 multi-layer project, considering development best practices, like SOLID and DRY, applying Domain-Driven concepts in a Onion Architecture.","archived":false,"fork":false,"pushed_at":"2023-02-21T08:57:48.000Z","size":4738,"stargazers_count":75,"open_issues_count":11,"forks_count":25,"subscribers_count":2,"default_branch":"release","last_synced_at":"2025-04-08T11:11:15.379Z","etag":null,"topics":["asp-core-mvc","asp-net-core","asp-net-core-5","asp-net-mvc5","ddd","dotnet","dotnet6","dry","efcore","efcore6","graphql","graphql-api","graphql-dotnet","graphql-server","onion-architecture","solid","web-mvc","webapi"],"latest_commit_sha":null,"homepage":"https://dev.to/antoniofalcao/about-a-net-5-rc1-web-application-with-graphql-3-5d3c","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AntonioFalcaoJr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-25T22:04:58.000Z","updated_at":"2025-04-08T04:28:00.000Z","dependencies_parsed_at":"2025-02-14T11:16:26.393Z","dependency_job_id":null,"html_url":"https://github.com/AntonioFalcaoJr/Dotnet6.GraphQL4.WebApplication","commit_stats":null,"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/AntonioFalcaoJr/Dotnet6.GraphQL4.WebApplication","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AntonioFalcaoJr%2FDotnet6.GraphQL4.WebApplication","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AntonioFalcaoJr%2FDotnet6.GraphQL4.WebApplication/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AntonioFalcaoJr%2FDotnet6.GraphQL4.WebApplication/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AntonioFalcaoJr%2FDotnet6.GraphQL4.WebApplication/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AntonioFalcaoJr","download_url":"https://codeload.github.com/AntonioFalcaoJr/Dotnet6.GraphQL4.WebApplication/tar.gz/refs/heads/release","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AntonioFalcaoJr%2FDotnet6.GraphQL4.WebApplication/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281319609,"owners_count":26481035,"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","status":"online","status_checked_at":"2025-10-27T02:00:05.855Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["asp-core-mvc","asp-net-core","asp-net-core-5","asp-net-mvc5","ddd","dotnet","dotnet6","dry","efcore","efcore6","graphql","graphql-api","graphql-dotnet","graphql-server","onion-architecture","solid","web-mvc","webapi"],"created_at":"2024-09-24T13:53:11.915Z","updated_at":"2025-10-27T18:30:16.674Z","avatar_url":"https://github.com/AntonioFalcaoJr.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dotnet6.GraphQL4.WebApplication\r\n\r\nThis project exemplifies the implementation and **dockerization** of a simple Razor Web MVC Core consuming a full **GraphQL 4** Web API, build in a **.NET 6** multi-layer project, considering development best practices, like **SOLID**, **KISS** and **DRY**, applying **Domain-Driven** concepts in a **Onion Architecture**.\r\n\r\n### Give a Star! :star:\r\n\r\n|WebAPI|\r\n|:----:| \r\n|[![Build/Test](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/workflows/Build/Test/badge.svg)](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/actions/workflows/build-and-test.yml) [![API Image](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/workflows/API%20Image/badge.svg)](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/actions/workflows/build-and-test.yml) [![Push API](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/workflows/Push%20API/badge.svg?branch=master)](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/actions/workflows/webapi-docker-publish.yml) [![CodeQL](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/workflows/CodeQL/badge.svg)](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/actions/workflows/codeql-analysis.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/35ba87f59d524982baeb803a3a9c42f7)](https://www.codacy.com/gh/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/dashboard?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=AntonioFalcao/Dotnet6.GraphQL4.WebApplication\u0026amp;utm_campaign=Badge_Grade) [![](https://img.shields.io/docker/pulls/antoniofalcaojr/dotnet6-graphql4-webapi?style=flat)](https://hub.docker.com/repository/docker/antoniofalcaojr/dotnet6-graphql4-webapi)|                \r\n|**WebMVC**|\r\n|[![Build/Test](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/workflows/Build/Test/badge.svg)](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/actions/workflows/build-and-test.yml) [![MVC Image](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/workflows/MVC%20Image/badge.svg)](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/actions/workflows/webmvc-docker-image.yml) [![Push MVC](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/workflows/Push%20MVC/badge.svg?branch=master)](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/actions/workflows/webmvc-docker-publish.yml) [![CodeQL](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/workflows/CodeQL/badge.svg)](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/actions/workflows/codeql-analysis.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/35ba87f59d524982baeb803a3a9c42f7)](https://www.codacy.com/gh/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/dashboard?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=AntonioFalcao/Dotnet6.GraphQL4.WebApplication\u0026amp;utm_campaign=Badge_Grade) [![](https://img.shields.io/docker/pulls/antoniofalcaojr/dotnet6-graphql4-webmvc?style=flat)](https://hub.docker.com/repository/docker/antoniofalcaojr/dotnet6-graphql4-webmvc)|\r\n\r\n**Oldest version**: Dotnet**5**.GraphQL**3**.WebApplication\r\n\r\n|WebAPI|[![](https://img.shields.io/docker/pulls/antoniofalcaojr/dotnet5-graphql3-webapi?style=flat)](https://hub.docker.com/repository/docker/antoniofalcaojr/dotnet5-graphql3-webapi)|\r\n|:----:|--- |\r\n|**WebMVC**|[![](https://img.shields.io/docker/pulls/antoniofalcaojr/dotnet5-graphql3-webmvc?style=flat)](https://hub.docker.com/repository/docker/antoniofalcaojr/dotnet5-graphql3-webmvc)|\r\n\r\n![home](./.assets/img/home.PNG)\r\n\r\n---\r\n\r\n![diagram](./.assets/img/diagram.png)\r\n\r\n---\r\n\r\n## Running\r\n\r\n### Development (secrets)\r\n \r\nTo configure database resource, `init` secrets in [`./src/Dotnet6.GraphQL4.Store.WebAPI`](./src/Dotnet6.GraphQL4.Store.WebAPI), and then define the `DefaultConnection`:\r\n\r\n```bash\r\ndotnet user-secrets init\r\ndotnet user-secrets set \"ConnectionStrings:DefaultConnection\" \"Server=localhost,1433;Database=Store;User=sa;Password=!MyComplexPassword\"\r\n```\r\n\r\nAfter this, to configure the HTTP client, `init` secrets in [`./src/Dotnet6.GraphQL4.Store.WebMVC`](./src/Dotnet6.GraphQL4.Store.WebMVC) and define **Store** client host:\r\n\r\n```bash\r\ndotnet user-secrets init\r\ndotnet user-secrets set \"HttpClient:Store\" \"http://localhost:5000\"\r\n```\r\n\r\n##### AppSettings\r\n\r\nIf you prefer, is possible to define it on WebAPI [`appsettings.Development.json`](./src/Dotnet6.GraphQL4.Store.WebAPI/appsettings.Development.json) and WebMVC [`appsettings.Development.json`](./src/Dotnet6.GraphQL4.Store.WebMVC/appsettings.Development.json) files:\r\n\r\nWebAPI\r\n\r\n```json5\r\n{\r\n  \"ConnectionStrings\": {\r\n    \"DefaultConnection\": \"Server=localhost,1433;Database=Store;User=sa;Password=!MyComplexPassword\"\r\n  }\r\n}\r\n```\r\n\r\nWebMCV\r\n\r\n```json5\r\n{\r\n  \"HttpClient\": {\r\n    \"Store\": \"http://localhost:5000\"\r\n  }\r\n}\r\n```\r\n\r\n### Production\r\n\r\nConsidering use Docker for CD (Continuous Deployment). On respective [compose](./docker-compose.yml) both web applications and sql server are in the same network, and then we can use named hosts. Already defined on WebAPI [`appsettings.json`](./src/Dotnet6.GraphQL4.Store.WebAPI/appsettings.json) and WebMVC [`appsettings.json`](./src/Dotnet6.GraphQL4.Store.WebMVC/appsettings.json) files:\r\n\r\n#### AppSettings\r\n\r\nWebAPI\r\n\r\n```json5\r\n{\r\n  \"ConnectionStrings\": {\r\n    \"DefaultConnection\": \"Server=mssql;Database=Store;User=sa;Password=!MyComplexPassword\"\r\n  }\r\n}\r\n```\r\n\r\nWebMCV\r\n\r\n```json5\r\n{\r\n  \"HttpClient\": {\r\n    \"Store\": \"http://webapi:5000\"\r\n  }\r\n}\r\n```\r\n\r\n### Docker\r\n\r\nThe [`./docker-compose.yml`](./docker-compose.yml) provide the `WebAPI`, `WebMVC` and `MS SQL Server` applications:\r\n\r\n```bash\r\ndocker-compose up -d\r\n``` \r\n\r\nIt's possible to run without a clone of the project using the respective compose:\r\n\r\n```yaml\r\nversion: \"3.7\"\r\n\r\nservices:\r\n  mssql:\r\n    container_name: mssql\r\n    image: mcr.microsoft.com/mssql/server\r\n    ports:\r\n      - 1433:1433\r\n    environment:\r\n      SA_PASSWORD: \"!MyComplexPassword\"\r\n      ACCEPT_EULA: \"Y\"\r\n    healthcheck:\r\n      test: /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P \"$$SA_PASSWORD\" -Q \"SELECT 1\" || exit 1\r\n      interval: 10s\r\n      timeout: 3s\r\n      retries: 10\r\n      start_period: 10s\r\n    networks:\r\n      - graphqlstore\r\n\r\n  webapi:\r\n    container_name: webapi\r\n    image: antoniofalcaojr/dotnet6-graphql4-webapi\r\n    environment:\r\n      - ASPNETCORE_URLS=http://*:5000\r\n    ports:\r\n      - 5000:5000\r\n    depends_on:\r\n      mssql:\r\n        condition: service_healthy\r\n    networks:\r\n      - graphqlstore\r\n\r\n  webmvc:\r\n    container_name: webmvc\r\n    image: antoniofalcaojr/dotnet6-graphql4-webmvc\r\n    environment:\r\n      - ASPNETCORE_URLS=http://*:7000\r\n    ports:\r\n      - 7000:7000\r\n    depends_on:\r\n      - webapi\r\n    networks:\r\n      - graphqlstore\r\n\r\n  healthchecks:\r\n    container_name: healthchecks-ui\r\n    image: xabarilcoding/healthchecksui\r\n    depends_on:\r\n      mssql:\r\n        condition: service_healthy\r\n    environment:\r\n      - storage_provider=SqlServer\r\n      - storage_connection=Server=mssql;Database=Store;User=sa;Password=!MyComplexPassword\r\n      - Logging:LogLevel:Default=Debug\r\n      - Logging:Loglevel:Microsoft=Warning\r\n      - Logging:LogLevel:HealthChecks=Debug\r\n      - HealthChecksUI:HealthChecks:0:Name=webapi\r\n      - HealthChecksUI:HealthChecks:0:Uri=http://webapi:5000/healthz\r\n      - HealthChecksUI:HealthChecks:1:Name=webmvc\r\n      - HealthChecksUI:HealthChecks:1:Uri=http://webmvc:7000/healthz\r\n    ports:\r\n      - 8000:80\r\n    networks:\r\n      - graphqlstore\r\n\r\nnetworks:\r\n  graphqlstore:\r\n    driver: bridge\r\n```\r\n### GraphQL Playground\r\n\r\nBy default **Playground** respond at `http://localhost:5000/ui/playground` but is possible configure the host and many others details in [`../DependencyInjection/Extensions/ApplicationBuilderExtensions.cs`](./src/Dotnet6.GraphQL4.Store.WebAPI/DependencyInjection/Extensions/ApplicationBuilderExtensions.cs)\r\n\r\n```c#\r\napp.UseGraphQLPlayground(\r\n       options: new() \r\n       {\r\n           BetaUpdates = true,\r\n           RequestCredentials = RequestCredentials.Omit,\r\n           HideTracingResponse = false,\r\n           EditorCursorShape = EditorCursorShape.Line,\r\n           EditorTheme = EditorTheme.Dark,\r\n           EditorFontSize = 14,\r\n           EditorReuseHeaders = true,\r\n           EditorFontFamily = \"JetBrains Mono\"\r\n       },\r\n       path: \"/ui/playground\");\r\n```\r\n\r\n### Health checks\r\n\r\nBased on cloud-native concepts, **Readiness** and **Liveness** integrity verification strategies were implemented. \r\n\r\n\u003e `/health`   \r\n\u003e Just check if the instance is running.\r\n\r\n\u003e `/health/live`  \r\n\u003e Check if the instance is running and all the dependencies too.\r\n\r\n\u003e `/health/ready`          \r\n\u003e Check if the instance and all the dependencies are ready to attend to all functionalities.\r\n\r\nWeb API\r\n\r\n`http://localhost:5000/health/ready`\r\n\r\n```json\r\n{\r\n  \"status\": \"Healthy\",\r\n  \"totalDuration\": \"00:00:00.2344435\",\r\n  \"entries\": {\r\n    \"Sql Server (Ready)\": {\r\n      \"data\": {},\r\n      \"duration\": \"00:00:00.2251420\",\r\n      \"status\": \"Healthy\",\r\n      \"tags\": [\r\n        \"ready\"\r\n      ]\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nWeb MVC\r\n\r\n`http://localhost:7000/health/ready`\r\n\r\n![Readiness](./.assets/img/ReadinessMVC.png)\r\n\r\n### Dump configuration\r\n\r\nIt is possible to dump the state of the environment configuration in through the middleware resource `/dump-config` in both applications.\r\n\r\n```c#\r\npublic void Configure(IApplicationBuilder app)\r\n{\r\n    app.UseEndpoints(endpoints =\u003e\r\n        {\r\n            endpoints.MapDumpConfig(\r\n                pattern: \"/dump-config\",\r\n                configurationRoot: _configuration as IConfigurationRoot,\r\n                isProduction: _env.IsProduction());\r\n        });\r\n}\r\n```\r\n___\r\n\r\n## Highlights\r\n\r\n### UnitOfWork + Execution Strategy + Transaction Scope\r\n\r\nThe implementation of the `UnitOfWork` gives support to the `ExecutionStrategy` from **EF Core** with `TransactionScope`.\r\n\r\n\u003e **operationAsync**: Encapsulates all desired transactions;    \r\n\u003e **condition**: External control for commitment;    \r\n\u003e **cancellationToken**: The cancellation token to be used within operation.   \r\n\r\n```c#\r\npublic Task\u003cReview\u003e AddReviewAsync(ReviewModel reviewModel, CancellationToken cancellationToken)\r\n{\r\n    return UnitOfWork.ExecuteInTransactionScopeAsync(\r\n        operationAsync: async ct =\u003e\r\n        {\r\n            var product = await Repository.GetByIdAsync(\r\n                id: reviewModel.ProductId,\r\n                include: products =\u003e products.Include(x =\u003e x.Reviews),\r\n                asTracking: true,\r\n                cancellationToken: ct);\r\n\r\n            var review = Mapper.Map\u003cReview\u003e(reviewModel);\r\n            product?.AddReview(review);\r\n            await OnEditAsync(product, ct);\r\n            return review;\r\n        },\r\n        condition: _ =\u003e NotificationContext.AllValidAsync,\r\n        cancellationToken: cancellationToken);      \r\n}\r\n```\r\n\r\n### Notifications (pattern/context)\r\n\r\nTo avoid handle exceptions, was implemented a [`NotificationContext`](./src/Dotnet6.GraphQL4.CrossCutting/Notifications/NotificationContext.cs) that's allow  all layers add business notifications through the request, with support to receive **Domain** notifications, that by other side, implementing validators from **Fluent Validation** and return a `ValidationResult`. \r\n\r\n```c#\r\nprotected bool OnValidate\u003cTEntity\u003e(TEntity entity, AbstractValidator\u003cTEntity\u003e validator)\r\n{\r\n    ValidationResult = validator.Validate(entity);\r\n    return IsValid;\r\n}\r\n\r\nprotected void AddError(string errorMessage, ValidationResult validationResult = default)\r\n{\r\n    ValidationResult.Errors.Add(new ValidationFailure(default, errorMessage));\r\n    validationResult?.Errors.ToList().ForEach(failure =\u003e ValidationResult.Errors.Add(failure));\r\n}\r\n```\r\n\r\nTo the **GraphQL** the notification context delivery a `ExecutionErrors` that is propagated to `result` from execution by a personalised [`Executer`](./src/Dotnet6.GraphQL4.Store.WebAPI/Graphs/Executers/StoreGraphQLExecuter.cs):  \r\n\r\n```c#\r\npublic override async Task\u003cExecutionResult\u003e ExecuteAsync(string operationName, string query, Inputs variables, IDictionary\u003cstring, object\u003e context, IServiceProvider requestServices, CancellationToken cancellationToken = new CancellationToken())\r\n{\r\n    var result = await base.ExecuteAsync(operationName, query, variables, context, requestServices, cancellationToken);\r\n    var notification = requestServices.GetRequiredService\u003cINotificationContext\u003e();\r\n\r\n    if (notification.HasNotifications is false) return result;\r\n\r\n    result.Errors = notification.ExecutionErrors;\r\n    result.Data = default;\r\n    \r\n    return result;\r\n}\r\n```\r\n\r\n### Resolving `Scoped` dependencies with `Singleton` Schema.\r\n\r\n_It's no more necessary after version 4.2.0 from  **GraphQL Server**. By default, the Service Provider is already being propagated._\r\n\r\n~~Is necessary, in the same personalised [`Executer`](./src/Dotnet6.GraphQL4.Store.WebAPI/Graphs/Executers/StoreGraphQLExecuter.cs) define the _service provider_ that will be used from `resolvers` on `fields`:~~\r\n\r\n```c#\r\nvar options = base.GetOptions(operationName, query, variables, context, cancellationToken);\r\noptions.RequestServices = _serviceProvider;\r\n```\r\n\r\n### Abstractions\r\n\r\nWith abstract designs, it is possible to reduce coupling in addition to applying **DRY** concepts, providing resources for the main behaviors:\r\n\r\n[`...Domain.Abstractions`](./src/Dotnet6.GraphQL4.Domain.Abstractions)\r\n\r\n```c#\r\npublic abstract class Entity\u003cTId\u003e\r\n    where TId : struct\r\n```\r\n\r\n```c#\r\npublic abstract class Builder\u003cTBuilder, TEntity, TId\u003e : IBuilder\u003cTEntity, TId\u003e\r\n    where TBuilder : Builder\u003cTBuilder, TEntity, TId\u003e\r\n    where TEntity : Entity\u003cTId\u003e\r\n    where TId : struct\r\n```\r\n    \r\n[`...Repositories.Abstractions`](./src/Dotnet6.GraphQL4.Repositories.Abstractions/Repository.cs)  \r\n\r\n```c#\r\npublic abstract class Repository\u003cTEntity, TId\u003e : IRepository\u003cTEntity, TId\u003e\r\n    where TEntity : Entity\u003cTId\u003e\r\n    where TId : struct\r\n{\r\n    private readonly DbSet\u003cTEntity\u003e _dbSet;\r\n\r\n    protected Repository(DbContext dbDbContext)\r\n    {\r\n        _dbSet = dbDbContext.Set\u003cTEntity\u003e();\r\n    }\r\n```\r\n\r\n[`...Services.Abstractions`](./src/Dotnet6.GraphQL4.Services.Abstractions/Service.cs) \r\n\r\n```c#\r\npublic abstract class Service\u003cTEntity, TModel, TId\u003e : IService\u003cTEntity, TModel, TId\u003e\r\n    where TEntity : Entity\u003cTId\u003e\r\n    where TModel : Model\u003cTId\u003e\r\n    where TId : struct\r\n{\r\n    protected readonly IMapper Mapper;\r\n    protected readonly INotificationContext NotificationContext;\r\n    protected readonly IRepository\u003cTEntity, TId\u003e Repository;\r\n    protected readonly IUnitOfWork UnitOfWork;\r\n\r\n    protected Service(\r\n        IUnitOfWork unitOfWork,\r\n        IRepository\u003cTEntity, TId\u003e repository,\r\n        IMapper mapper,\r\n        INotificationContext notificationContext)\r\n    {\r\n        UnitOfWork = unitOfWork;\r\n        Repository = repository;\r\n        Mapper = mapper;\r\n        NotificationContext = notificationContext;\r\n    }\r\n```\r\n\r\n```c#\r\npublic abstract class MessageService\u003cTMessage, TModel, TId\u003e : IMessageService\u003cTMessage, TModel, TId\u003e\r\n    where TMessage : class\r\n    where TModel : Model\u003cTId\u003e\r\n    where TId : struct\r\n{\r\n    private readonly IMapper _mapper;\r\n    private readonly ISubject\u003cTMessage\u003e _subject;\r\n\r\n    protected MessageService(IMapper mapper, ISubject\u003cTMessage\u003e subject)\r\n    {\r\n        _mapper = mapper;\r\n        _subject = subject;\r\n    }\r\n```\r\n\r\n### From `EF TPH` to `GraphQL Interface`\r\n\r\nENTITY\r\n\r\n```c#\r\npublic class ProductConfig : IEntityTypeConfiguration\u003cProduct\u003e\r\n{\r\n    public void Configure(EntityTypeBuilder\u003cProduct\u003e builder)\r\n    {\r\n        builder\r\n            .HasDiscriminator()\r\n            .HasValue\u003cBoot\u003e(nameof(Boot))\r\n            .HasValue\u003cKayak\u003e(nameof(Kayak))\r\n            .HasValue\u003cBackpack\u003e(nameof(Backpack));\r\n    }\r\n}\r\n```\r\n\r\nINHERITOR\r\n\r\n```c#\r\npublic class KayakConfig : IEntityTypeConfiguration\u003cKayak\u003e\r\n{\r\n    public void Configure(EntityTypeBuilder\u003cKayak\u003e builder)\r\n    {\r\n        builder\r\n            .HasBaseType\u003cProduct\u003e();\r\n    }\r\n}\r\n```\r\n\r\nINTERFACE \r\n\r\n```c#\r\npublic sealed class ProductInterfaceGraphType : InterfaceGraphType\u003cProduct\u003e\r\n{\r\n    public ProductInterfaceGraphType(BootGraphType bootGraphType, BackpackGraphType backpackGraphType, KayakGraphType kayakGraphType)\r\n    {\r\n        Name = \"product\";\r\n\r\n        ResolveType = @object =\u003e\r\n        {\r\n            return @object switch\r\n            {\r\n                Boot _ =\u003e bootGraphType,\r\n                Backpack _ =\u003e backpackGraphType,\r\n                Kayak _ =\u003e kayakGraphType,\r\n                _ =\u003e default\r\n            };\r\n        };\r\n    }\r\n}\r\n```\r\n\r\nOBJECT\r\n\r\n```c#\r\npublic sealed class KayakGraphType : ObjectGraphType\u003cKayak\u003e\r\n{\r\n    public KayakGraphType()\r\n    {\r\n        Name = \"kayak\";\r\n        Interface\u003cProductInterfaceGraphType\u003e();\r\n        IsTypeOf = o =\u003e o is Product;\r\n    }\r\n}\r\n```\r\n\r\n___\r\n\r\n\r\n## Queries\r\n\r\n#### Fragment for comparison and Arguments\r\n\r\nQUERY\r\n\r\n```markdown\r\n{\r\n  First: product(id: \"2c05b59b-8fb3-4cba-8698-01d55a0284e5\") {\r\n    ...comparisonFields\r\n  }\r\n  Second: product(id: \"65af82e8-27f6-44f3-af4a-029b73f14530\") {\r\n    ...comparisonFields\r\n  }\r\n}\r\n\r\nfragment comparisonFields on product {\r\n  id\r\n  name\r\n  rating\r\n  description\r\n}\r\n```\r\nRESULT\r\n\r\n```json5\r\n{\r\n  \"data\": {\r\n    \"First\": {\r\n      \"id\": \"2c05b59b-8fb3-4cba-8698-01d55a0284e5\",\r\n      \"name\": \"libero\",\r\n      \"rating\": 5,\r\n      \"description\": \"Deleniti voluptas quidem accusamus est debitis quisquam enim.\"\r\n    },\r\n    \"Second\": {\r\n      \"id\": \"65af82e8-27f6-44f3-af4a-029b73f14530\",\r\n      \"name\": \"debitis\",\r\n      \"rating\": 10,\r\n      \"description\": \"Est veniam unde.\"\r\n    }\r\n  }\r\n}\r\n```\r\n___\r\n\r\n#### Query named's and Variables\r\n\r\nQUERY\r\n\r\n```markdown\r\nquery all {\r\n  products {\r\n    items {\r\n      id\r\n      name\r\n    }\r\n  }\r\n}\r\n\r\nquery byid($productId: Guid!) {\r\n  product(id: $productId) {\r\n    id\r\n    name\r\n  }\r\n}\r\n```\r\n\r\nVARIABLES\r\n\r\n```json5\r\n{\r\n  \"productId\": \"2c05b59b-8fb3-4cba-8698-01d55a0284e5\"\r\n}\r\n```\r\n\r\nHTTP BODY\r\n\r\n```markdown\r\n{\r\n    \"operationName\": \"byid\",\r\n    \"variables\": {\r\n        \"productId\": \"2c05b59b-8fb3-4cba-8698-01d55a0284e5\"\r\n    },\r\n    \"query\": \"query all {\r\n        products {\r\n          items {          \r\n            id\r\n            name\r\n         }\r\n       }\r\n    }\r\n    query byid($productId: Guid!) {\r\n        product(id: $productId) {\r\n          id\r\n          name\r\n        }\r\n    }\"\r\n}\r\n```\r\n\r\nPLAYGROUND\r\n\r\n![queries](./.assets/img/queries.PNG) \r\n___\r\n#### Variables with include, skip and default value\r\n\r\nQUERY\r\n\r\n```markdown\r\nquery all($showPrice: Boolean = false) {\r\n  products {\r\n    items {\r\n      id\r\n      name\r\n      price @include(if: $showPrice)\r\n      rating @skip(if: $showPrice)\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nVARIABLES\r\n\r\n```json5\r\n{\r\n  \"showPrice\": true\r\n}\r\n```\r\n\r\nHTTP BODY\r\n\r\n```markdown\r\n{\r\n    \"operationName\": \"all\",\r\n    \"variables\": {\r\n        \"showPrice\": false\r\n    },\r\n    \"query\": \"query all($showPrice: Boolean = false) {\r\n          products {\r\n            items {\r\n              id\r\n              name\r\n              price @include(if: $showPrice)\r\n              rating @skip(if: $showPrice)\r\n            }\r\n        }\r\n    }\"\r\n}\r\n```\r\n___\r\n### Pagination\r\n\r\nQUERY\r\n\r\n```markdown\r\n{\r\n  products(pageParams: { index: 2, size: 1 }) {\r\n    items {\r\n      id\r\n    }\r\n    pageInfo {\r\n      current\r\n      hasNext\r\n      hasPrevious\r\n      size\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nRESULT\r\n\r\n```json5\r\n{\r\n  \"data\": {\r\n    \"products\": {\r\n      \"items\": [\r\n        {\r\n          \"id\": \"3b2f6ce4-1b1d-4376-80a6-0b8d51932757\"\r\n        }\r\n      ],\r\n      \"pageInfo\": {\r\n        \"current\": 2,\r\n        \"hasNext\": true,\r\n        \"hasPrevious\": true,\r\n        \"size\": 1\r\n      }\r\n    }\r\n  }\r\n}\r\n```\r\n\r\n___\r\n\r\n## Mutations\r\n\r\nMUTATION\r\n\r\n\u003e Creating / adding a new Review to the respective product.\r\n\r\n```markdown\r\nmutation($review: reviewInput!) {\r\n  createReview(review: $review) {\r\n    id\r\n  }\r\n}\r\n```\r\nVARIABLES\r\n\r\n```json5\r\n{\r\n  \"review\": {\r\n    \"title\": \"some title\",\r\n    \"comment\": \"some comment\",\r\n    \"productId\": \"0fb8ec7e-7af1-4fe3-a2e2-000996ffd20f\"\r\n  }\r\n}\r\n```\r\n\r\nRESULT\r\n\r\n```json5\r\n{\r\n  \"data\": {\r\n    \"createReview\": {\r\n      \"title\": \"some title\"\r\n    }\r\n  }\r\n}\r\n```\r\n___\r\n\r\n## Subscriptions\r\n\r\nSUBSCRIPTION\r\n\r\n\u003e The Mutation stay listening if a new review is added.\r\n\r\n```markdown\r\nsubscription {\r\n  reviewAdded {\r\n    title\r\n  }\r\n}\r\n\r\n```\r\nRESULT\r\n\r\n```json5\r\n{\r\n  \"data\": {\r\n    \"reviewAdded\": {\r\n      \"title\": \"Some title\"\r\n    }\r\n  }\r\n}\r\n```\r\n___\r\n\r\n## Built With\r\n\r\n### Microsoft Stack - v6 - preview 2\r\n\r\n* [.NET 6](https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-2/) - Base framework;\r\n* [ASP.NET 6](https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-2/) - Web framework;\r\n* [Entity Framework Core 6](https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-2/) - ORM;\r\n* [Microsoft SQL Server on Linux for Docker](https://hub.docker.com/_/microsoft-mssql-server) - Database.\r\n\r\n### GraphQL Stack - v4\r\n\r\n* [GraphQL](https://graphql.org/) - GraphQL is a query language for APIs and a runtime for fulfilling those queries with data;\r\n* [GraphQL for .NET](https://github.com/graphql-dotnet/graphql-dotnet/) - This is an implementation of GraphQL in .NET;\r\n* [GraphQL Client](https://github.com/graphql-dotnet/graphql-client) - A GraphQL Client for .NET over HTTP;\r\n* [GraphQL Playground](https://github.com/prisma-labs/graphql-playground/) - GraphQL IDE for better development workflows.\r\n\r\n### Community Stack\r\n\r\n* [AutoMapper](https://automapper.org/) - A convention-based object-object mapper;\r\n* [FluentValidation](https://fluentvalidation.net/) - A popular .NET library for building strongly-typed validation rules;\r\n* [Bogus](https://github.com/bchavez/Bogus) - A simple and sane fake data generator for C#, F#, and VB.NET;\r\n* [Bootstrap](https://getbootstrap.com/) - The most popular HTML, CSS, and JS library in the world.\r\n* [Serilog](https://serilog.net/) - Serilog provides diagnostic logging to files, the console, and elsewhere.\r\n\r\n## Contributing\r\n\r\nAll contributions are welcome. Please take a look at [contributing](./CONTRIBUTING.md) guide.\r\n\r\n## Versioning\r\n\r\nWe use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/tags).\r\n\r\n## Authors\r\n\r\n\u003e See the list of [contributors](https://github.com/AntonioFalcao/Dotnet6.GraphQL4.WebApplication/graphs/contributors) who participated in this project.\r\n\r\n## License\r\n\r\nThis project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantoniofalcaojr%2Fdotnet6.graphql4.webapplication","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantoniofalcaojr%2Fdotnet6.graphql4.webapplication","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantoniofalcaojr%2Fdotnet6.graphql4.webapplication/lists"}