WebApi + ClientApp, GraphQL, Reflection

This commit is contained in:
Boris Milašinović
2026-05-06 20:55:05 +02:00
parent 8f7c704a90
commit 4fb3de19f6
196 changed files with 10395 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>PI</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="GraphQL.Server.Ui.GraphiQL" Version="8.3.3" />
<PackageReference Include="GraphQL.Server.Ui.Voyager" Version="8.3.3" />
<PackageReference Include="HotChocolate.AspNetCore" Version="16.0.0" />
<PackageReference Include="HotChocolate.Data.EntityFramework" Version="16.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\\Events.EF\\Events.EF.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,53 @@
using Events.EF.Data.Postgres;
using GraphQL.Server.Ui.Voyager;
using GraphQLServer.SetupGraphQL;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
#region Configure services
builder.Services.AddDbContext<EventsContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("EventsPostgres")));
builder.Services.AddGraphQLServer()
.ModifyRequestOptions(opt => opt.IncludeExceptionDetails = builder.Environment.IsDevelopment())
.ModifyPagingOptions(options =>
{
options.DefaultPageSize = 20;
options.MaxPageSize = 1000;
options.IncludeTotalCount = true;
})
.AddProjections()
.AddFiltering()
.AddSorting()
.AddQueryType<Queries>()
.AddMutationType<Mutations>();
#endregion
var app = builder.Build();
#region Configure middleware pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseRouting();
app.MapGraphQL();
app.UseGraphQLVoyager("/voyager", new VoyagerOptions() { GraphQLEndPoint = "graphql" });
app.UseGraphQLGraphiQL(
"/",
new GraphQL.Server.Ui.GraphiQL.GraphiQLOptions
{
GraphQLEndPoint = "/graphql",
SubscriptionsEndPoint = "/graphql",
});
#endregion
app.Run();

View File

@@ -0,0 +1,14 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:50440",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,5 @@
namespace GraphQLServer.SetupGraphQL;
public record EventInput(
string Name,
DateOnly EventDate);

View File

@@ -0,0 +1,50 @@
using Events.EF.Data.MSSQL;
using Events.EF.Models;
namespace GraphQLServer.SetupGraphQL;
public partial class Mutations
{
public async Task<Event> AddEvent([Service] EventsContext ctx, EventInput input)
{
var item = new Event
{
Name = input.Name,
EventDate = input.EventDate
};
ctx.Events.Add(item);
await ctx.SaveChangesAsync();
return item;
}
public async Task<Event?> UpdateEvent([Service] EventsContext ctx, int id, EventInput input)
{
var item = await ctx.Events.FindAsync(id);
if (item is null)
{
return null;
}
item.Name = input.Name;
item.EventDate = input.EventDate;
await ctx.SaveChangesAsync();
return item;
}
public async Task<bool> DeleteEvent([Service] EventsContext ctx, int id)
{
var item = await ctx.Events.FindAsync(id);
if (item is null)
{
return false;
}
ctx.Events.Remove(item);
await ctx.SaveChangesAsync();
return true;
}
}

View File

@@ -0,0 +1,5 @@
namespace GraphQLServer.SetupGraphQL;
public partial class Mutations
{
}

View File

@@ -0,0 +1,14 @@
using Events.EF.Models;
namespace GraphQLServer.SetupGraphQL;
public class PeoplePage
{
public required int TotalCount { get; init; }
public required int PageNumber { get; init; }
public required int PageSize { get; init; }
public required IReadOnlyList<Person> Items { get; init; }
}

View File

@@ -0,0 +1,74 @@
using Events.EF.Data.MSSQL;
using Events.EF.Models;
using Microsoft.EntityFrameworkCore;
namespace GraphQLServer.SetupGraphQL;
public class Queries
{
[UsePaging]
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Country> GetCountries([Service] EventsContext ctx) => ctx.Countries.AsNoTracking();
[UsePaging]
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Sport> GetSports([Service] EventsContext ctx) => ctx.Sports.AsNoTracking();
[UsePaging]
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Person> GetPeople([Service] EventsContext ctx) => ctx.People.AsNoTracking();
public async Task<PeoplePage> GetPeoplePage(
[Service] EventsContext ctx,
int pageNumber = 1,
int pageSize = 10)
{
pageNumber = Math.Max(1, pageNumber);
pageSize = Math.Clamp(pageSize, 1, 100);
var baseQuery = ctx.People
.AsNoTracking()
.OrderBy(p => p.Id);
var totalCount = await baseQuery.CountAsync();
var items = await baseQuery
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToListAsync();
return new PeoplePage
{
TotalCount = totalCount,
PageNumber = pageNumber,
PageSize = pageSize,
Items = items
};
}
[UsePaging]
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Event> GetEvents([Service] EventsContext ctx) => ctx.Events.AsNoTracking();
[UsePaging]
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Event> GetEventsForDate([Service] EventsContext ctx, DateOnly date) =>
ctx.Events.AsNoTracking()
.Where(e => e.EventDate == date);
[UsePaging]
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Registration> GetRegistrations([Service] EventsContext ctx) => ctx.Registrations.AsNoTracking();
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"EventsMssql": "Data Source=.,1433;Initial Catalog=Events;User Id=sport;Password=go and look in the secrets file;TrustServerCertificate=True"
},
"AllowedHosts": "*"
}