refactor: decouple scoreboard api logic

Now scoreboard business logic is in ScoreboardService.cs and scoreboard api login is in ScoreboardController.cs
This commit is contained in:
cuqmbr 2022-07-16 16:34:26 +03:00
parent 352a5c89ba
commit 4adf64d129
5 changed files with 117 additions and 30 deletions

1
.gitignore vendored
View File

@ -454,3 +454,4 @@ $RECYCLE.BIN/
!.vscode/extensions.json !.vscode/extensions.json
*.db *.db
*.db*

View File

@ -1,33 +1,35 @@
using DatabaseModels; using System.Data;
using DatabaseModels.Plain;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Server.Data; using Server.Data;
using Server.Services;
namespace Server.Controllers; namespace Server.Controllers;
[Route("api/[controller]")] [Route("[controller]")]
[ApiController] [ApiController]
public class ScoreboardController : ControllerBase public class ScoreboardController : ControllerBase
{ {
private readonly ServerDbContext _context; private readonly ScoreboardService _sbService;
public ScoreboardController(ServerDbContext context) public ScoreboardController(ScoreboardService sbService)
{ {
_context = context; _sbService = sbService;
} }
// GET: api/Scoreboard // GET: /scoreboard
[HttpGet] [HttpGet]
public async Task<ActionResult<ScoreboardRecord[]>> Get() public async Task<ActionResult<ScoreboardRecord[]>> Get()
{ {
return await _context.Scoreboard.ToArrayAsync(); return await _sbService.GetScoreboard();
} }
// GET: api/Scoreboard/cuqmbr // GET: /scoreboard/cuqmbr
[HttpGet("{username}", Name = "Get")] [HttpGet("{username}", Name = "Get")]
public async Task<ActionResult<ScoreboardRecord>> Get(string username) public async Task<ActionResult<ScoreboardRecord>> Get(string username)
{ {
var sbRecord = await _context.Scoreboard.FirstOrDefaultAsync(sbr => sbr.Username == username); var sbRecord = await _sbService.GetUserHighScore(username);
if (sbRecord == null) if (sbRecord == null)
{ {
@ -37,46 +39,50 @@ public class ScoreboardController : ControllerBase
return sbRecord; return sbRecord;
} }
// POST: api/Scoreboard // POST: /scoreboard
[HttpPost] [HttpPost]
public async Task<ActionResult> Post([FromBody] ScoreboardRecord sbRecord) public async Task<ActionResult> Post([FromBody] ScoreboardRecord sbRecord)
{ {
await _context.AddAsync(sbRecord); await _sbService.AddUserHighScore(sbRecord);
await _context.SaveChangesAsync(); return CreatedAtAction(nameof(Get), new {sbRecord.User}, sbRecord);
return CreatedAtAction(nameof(Get), new {sbRecord.Username}, sbRecord);
} }
// PUT: api/Scoreboard/cuqmbr // PUT: /scoreboard/id
[HttpPut("{username}")] [HttpPut("{id}", Name = "Put")]
public async Task<ActionResult> Put(string username, [FromBody] ScoreboardRecord sbRecord) public async Task<ActionResult<ScoreboardRecord>> Put(int id, [FromBody] ScoreboardRecord sbRecord)
{ {
if (username != sbRecord.Username) if (id != sbRecord.Id)
{ {
return BadRequest(); return BadRequest();
} }
_context.Entry(sbRecord).State = EntityState.Modified;
try try
{ {
await _context.SaveChangesAsync(); await _sbService.UpdateScoreboardRecord(sbRecord);
} }
catch (DbUpdateConcurrencyException) catch (DBConcurrencyException)
{ {
if (!await ScoreboardRecordExists(sbRecord.Username)) if (!await _sbService.ScoreboardRecordExists(id))
{ {
return NotFound(); return NotFound();
} }
throw; throw;
} }
return NoContent(); return NoContent();
} }
private async Task<bool> ScoreboardRecordExists(string username) // DELETE: /scoreboard/id
[HttpDelete("{id}", Name = "Delete")]
public async Task<ActionResult> Delete(int id)
{ {
return await _context.Scoreboard.AnyAsync(sbr => sbr.Username == username); if (!await _sbService.ScoreboardRecordExists(id))
{
return NotFound();
}
await _sbService.DeleteScoreboardRecord(id);
return NoContent();
} }
} }

View File

@ -1,15 +1,23 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Server.Data; using Server.Data;
using Server.Services;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
builder.Services.AddControllers(); builder.Services.AddControllers().AddNewtonsoftJson(o => {
o.SerializerSettings.ContractResolver = new DefaultContractResolver();
o.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<ServerDbContext>(o => o.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection"))); builder.Services.AddDbContext<ServerDbContext>(o =>
o.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<ScoreboardService>();
var app = builder.Build(); var app = builder.Build();

View File

@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.6"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.6">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@ -0,0 +1,71 @@
using DatabaseModels.Plain;
using Microsoft.EntityFrameworkCore;
using Server.Data;
namespace Server.Services;
public class ScoreboardService
{
private readonly ServerDbContext _dbContext;
public ScoreboardService(ServerDbContext dbContext)
{
_dbContext = dbContext;
}
// GET
public async Task<ScoreboardRecord[]> GetScoreboard()
{
var sbRecords = await _dbContext.Scoreboard
.Include(sbr => sbr.User)
.ToListAsync();
sbRecords.Sort((a, b) => b.Score - a.Score);
return sbRecords.DistinctBy(sbr => sbr.User.Id).ToArray();
}
public async Task<ScoreboardRecord?> GetUserHighScore(string username)
{
var userScoreboardRecords = await _dbContext.Scoreboard
.Include(sbr => sbr.User)
.Where(sbr => sbr.User.Username == username)
.ToListAsync();
return userScoreboardRecords.MaxBy(sbr => sbr.Score);
}
// POST
public async Task AddUserHighScore(ScoreboardRecord sbRecord)
{
var dbUser = await _dbContext.Users.FindAsync(sbRecord.User.Id);
sbRecord.User = dbUser;
await _dbContext.AddAsync(sbRecord);
await _dbContext.SaveChangesAsync();
}
// PUT
public async Task UpdateScoreboardRecord(ScoreboardRecord sbRecord)
{
_dbContext.Entry(sbRecord).State = EntityState.Modified;
await _dbContext.SaveChangesAsync();
}
public async Task<bool> ScoreboardRecordExists(int id)
{
return await _dbContext.Scoreboard.AnyAsync(sbr => sbr.Id == id);
}
// DELETE
public async Task DeleteScoreboardRecord(int id)
{
var sbRecord = await _dbContext.Scoreboard.FindAsync(id);
_dbContext.Scoreboard.Remove(sbRecord!);
await _dbContext.SaveChangesAsync();
}
}