GraphQL - ASP.NET Core com HotChocolate - II
 Neste post veremos como implementar o GraphQL em uma aplicação ASP .NET Core usando o HotChocolate.


Continuando a primeira parte do artigo vamos implementar agora o contrato de consulta, manipulação e os ouvintes dos eventos no GraphQL.
 

 



Podemos fazer uma relação entre os conceitos usados em uma API REST com os do GraphQL.

 

Em uma API REST, usamos GET para buscar dados e usamos POST, PUT DELETE para modificar o estado dos dados. Assim podemos dizer que : 

Criando os objetos GraphQL

Vamos criar no projeto a pasta DataAccessGraphQL e vamos inciar criando a classe Query:

1- Query
 

using GraphQLNet.Models;
using GraphQLNet.Repositories;
using HotChocolate.Subscriptions;
namespace GraphQLNet.DataAccessGraphQL;
public class Query
{
    public List<Funcionario> TodosFuncionarios([Service]
        IFuncionarioRepository funcionarioRepository) =>
        funcionarioRepository.GetFuncionarios();
    public List<Funcionario> TodosFuncionariosComDepartamentos([Service] 
        IFuncionarioRepository funcionarioRepository) =>
        funcionarioRepository.GetFuncionariosComDepartamento();
    public async Task<Funcionario> FuncionarioPeloId([Service] 
        IFuncionarioRepository funcionarioRepository,
        [Service] ITopicEventSender eventSender, int id)
    {
        Funcionario funcionarioRetornado = funcionarioRepository.GetFuncionarioPorId(id);
        await eventSender.SendAsync("FuncionarioRetornado", funcionarioRetornado);
        return funcionarioRetornado;
    }

    public List<Departamento> TodosDepartamentos([Service] 
      IDepartamentoRepository departamentoRepository) =>
    departamentoRepository.GetListaDepartamentos();
    public List<Departamento> DepartamentosComFuncionarios([Service] 
         IDepartamentoRepository departamentoRepository) =>
        departamentoRepository.GetDepartamentosComFuncionarios();
}

 

Nesta classe temos os seguintes métodos:

  1. TodosFuncionarios() que retorna todos os Funcionarios cadastrados;

  2. TodosFuncionariosComDepartamentos() que retorna os funcionários e seus departamentos;

  3. FuncionarioPeloId(int id) retorna um funcionário pelo seu Id;

  4. TodosDepartamentos() retorna todos os departamentos;

  5. DepartamentosComFuncionarios() retorna os departamentos e seus funcionários;

Observe que injetamos instâncias dos repositórios IFuncionarioRepository e IDepartamentoRepository para poder usar os métodos que irão obter os dados.

 

O método que retorna o funcionário pelo id usa o parâmetro ITopicEventSender eventSender que é usado para criar uma assinatura com o Tópico "FuncionarioRetornado". Sempre que esse método é chamado, o evento é enviado usando eventSender.SendAsync() e qualquer método que se inscreva nesse evento receberá os dados.

 

2- Mutation

 

A seguir vamos criar nesta pasta a classe Mutation que vão conter todas as Mutations ou manipulação dos dados:

 

using GraphQLNet.Models;
using GraphQLNet.Repositories;
using HotChocolate.Subscriptions;
namespace GraphQLNet.DataAccessGraphQL;
public class Mutation
{
    public async Task<Departamento> CriarDepartamento([Service] 
                 IDepartamentoRepository departamentoRepository,
                 [Service] ITopicEventSender eventSender, string nomeDepartamento)
    {
        var novoDepartamento = new Departamento
        {
            Nome = nomeDepartamento
        };
        var departamentoCriado = await departamentoRepository.CriarDepartamento(novoDepartamento);
        await eventSender.SendAsync("DepartamentoCriado", departamentoCriado);
        return departamentoCriado;
    }
    public async Task<Funcionario> CriaFuncionarioComDepartamentoId([Service] 
                 IFuncionarioRepository funcionarioRepository,
                 string nome, int idade, string email, int departamentoId)
    {
        Funcionario novoFuncionario = new Funcionario
        {
            Nome = nome,
            Idade = idade,
            Email = email,
            DepartamentoId = departamentoId
        };
        var funcionarioCriado = await funcionarioRepository.CriarFuncionario(novoFuncionario);
        return funcionarioCriado;
    }
    public async Task<Funcionario> CriaFuncionarioComDepartamento([Service] 
                 IFuncionarioRepository funcionarioRepository,
                 string nome, int idade, string email, string departamentoNome)
    {
        Funcionario novoFuncionario = new Funcionario
        {
            Nome = nome,
            Idade = idade,
            Email = email,
            Departamento = new Departamento { Nome = departamentoNome }
        };
        var funcinarioCriado = await funcionarioRepository.CriarFuncionario(novoFuncionario);
        return funcinarioCriado;
    }
}


Nesta classe criamos :

 

O método CriarDepartamento() onde o parâmetro nomeDepartamento será fornecido pelo usuário. O parâmetro ITopicEventSender eventSender é usado para criar uma assinatura com o Tópico "DepartamentoCriado".

A seguir criamos o método
CriaFuncionarioComDepartamentoId() que pode ser usado para criar um novo Funcionario passando um DepartomentoId existente, ou seja, criar um Funcionario com um departamento existente. Os parâmetros nome: string, idade: int, email: string e departomentoId: int serão fornecidos pelo usuário. Observe que configuramos o DepartamentoId para o departomentoId passado pelo usuário.

Depois criamos o método
CriaFuncionarioComDepartamento() que é usado para criar um novo funcionário. No processo de criação do Funcionário, um novo Departamento é criado e a propriedade DepartamentoId deste Funcionário é definida automaticamente para o DepartamentoId do Departamento recém-criado.

 

2- Subscription

 

Para concluir vamos criar nesta pasta a classe Subscription.

 

As subscriptions ou assinaturas são usadas para configurar ouvintes de eventos, por exemplo. podemos configurar um ouvinte de eventos que responde quando criamos um novo Departamento ou Consultamos o banco de dados para um Funcionário:

 

using GraphQLNet.Models;
using HotChocolate.Execution;
using HotChocolate.Subscriptions;
namespace GraphQLNet.DataAccessGraphQL;
public class Subscription
{
    [SubscribeAndResolve]
    public async ValueTask<ISourceStream<Departamento>> OnDepartamentoCreate([Service] 
                           ITopicEventReceiver eventReceiver,CancellationToken cancellationToken)
    {
        return await eventReceiver.SubscribeAsync<string, Departamento>("DepartamentoCriado", cancellationToken);
    }
    [SubscribeAndResolve]
    public async ValueTask<ISourceStream<Funcionario>> OnFuncionarioGet([Service] 
                           ITopicEventReceiver eventReceiver,CancellationToken cancellationToken)
    {
        return await eventReceiver.SubscribeAsync<string, Funcionario>("FuncionarioRetornado", cancellationToken);
    }
}


Criamos os eventos
OnDepartamentoCreate que define um assinante do tópico : "DepartamentoCriado", e evento OnFuncionarioGet que define um assinante do tópico "FuncionarioRetornado".

 

Com isso concluímos a criação dos recursos que iremos usar no GraphQL.

 

Configurando o GraphQL

 

Precisamos agora configurar o GraphQL para poder usá-lo. Para isso vamos incluir no arquivo Program as linhas de código destacadas abaixo:

 

using GraphQLNet.DataAccessGraphQL;
using GraphQLNet.DataContext;
using GraphQLNet.Repositories;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddDbContext<AppDbContext>(options =>
              options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddInMemorySubscriptions();
builder.Services
    .AddGraphQLServer()
    .AddQueryType<Query>()
    .AddMutationType<Mutation>()
.AddSubscriptionType<Subscription>();
builder.Services.AddScoped<IFuncionarioRepository, FuncionarioRepository>();
builder.Services.AddScoped<IDepartamentoRepository, DepartamentoRepository>();
string AllowedOrigin = "allowedOrigin";
builder.Services.AddCors(option =>
{
    option.AddPolicy("AllowedOrigin",
        builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()
        );
});
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseCors(AllowedOrigin);
app.UseWebSockets();
app.UseRouting()
    .UseEndpoints(endpoints =>
    {
        endpoints.MapGraphQL();
    });
app.Run();

 

Vamos entender o código incluído:

 

- Registramos o serviço par ao armazenamento na memória do aplicativo usado pelas subscriptions do GraphQL;

 

- Adicionamos o serviço  para o GraphQL, Query, Mutation e Subscription onde AddQueryType(), AddMutationType() e AddSubscriptionType() recebem as classes Query, Mutation e Subscription  respectivamente que foram criadas.

 

- Incluímos o middleware para dar suporte a comunicação com Sockets;

 

- Mapeamos o endpoint para usar o GraphQL;

 

Agora temos tudo pronto para poder usar o GraphQL em nosso projeto ASP .NET Core.

Na próxima parte do artigo vamos realizar os testes usando os recursos das classes Query, Mutation e Subscription criadas.

 

"Miserável homem que eu sou! quem me livrará do corpo desta morte ?
Dou graças a Deus por Jesus Cristo nosso Senhor. Assim que eu mesmo com o entendimento sirvo à lei de Deus, mas com a carne à lei do pecado. "
Romanos 7:24,25

Porque um menino nos nasceu, um filho se nos deu, e o principado está sobre os seus ombros, e se chamará o seu nome: Maravilhoso, Conselheiro, Deus Forte, Pai da Eternidade, Príncipe da Paz.

Isaías 9:6
Porque um menino nos nasceu, um filho se nos deu, e o principado está sobre os seus ombros, e se chamará o seu nome: Maravilhoso, Conselheiro, Deus Forte, Pai da Eternidade, Príncipe da Paz.

Isaías 9:6

Referências:


José Carlos Macoratti