Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions BLL/Services/ITodoItemService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using TodoApi.Models;
using TodoApiDTO.DAL.Entities;

namespace TodoApiDTO.BLL.Services
{
public interface ITodoItemService
{
Task<IEnumerable<TodoItemDTO>> GetTodoItemsAsync();
Task<TodoItemDTO> GetTodoItemAsync(long id);
Task<TodoItemDTO> AddAsync(TodoItemDTO todoItemDTO);
Task EditAsync(TodoItemDTO todoItemDTO);
Task DeleteAsync(long id);
}
}
78 changes: 78 additions & 0 deletions BLL/Services/Implementations/TodoItemService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
using TodoApiDTO.BLL.Services;
using TodoApiDTO.DAL.Entities;
using TodoApiDTO.DAL.Repositories;


namespace TodoApiDTO.BLL.Implementations.Services
{
public class TodoItemService : ITodoItemService
{
private readonly ITodoItemRepository repository;

public TodoItemService(ITodoItemRepository repository)
{
this.repository = repository;
}

public async Task<IEnumerable<TodoItemDTO>> GetTodoItemsAsync()
{
var todoItems = await repository.GetTodoItemsAsync();
return todoItems
.Select(x => ItemToDTO(x));
}

public async Task<TodoItemDTO> GetTodoItemAsync(long id)
{
var todoItem = await repository.GetTodoItemByIdAsync(id);
var todoItemDTO = todoItem != null ? ItemToDTO(todoItem) : null;
return todoItemDTO;
}
public async Task<TodoItemDTO> AddAsync(TodoItemDTO todoItemDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoItemDTO.IsComplete,
Name = todoItemDTO.Name
};

await repository.InsertTodoItemAsync(todoItem);
await repository.SaveAsync();
return ItemToDTO(todoItem);
}
public async Task EditAsync(TodoItemDTO todoItemDTO)
{
await repository.UpdateTodoItemAsync(todoItemDTO);

try
{
await repository.SaveAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(todoItemDTO.Id))
{
throw new TodoItemException("Item not found");
}
}
public async Task DeleteAsync(long id)
{
await repository.DeleteTodoItemAsync(id);
await repository.SaveAsync();


}
private bool TodoItemExists(long id) =>
repository.GetTodoItemsAsync().Result.Any(e => e.Id == id);

private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
}
15 changes: 15 additions & 0 deletions BLL/TodoItemException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;

namespace TodoApiDTO.BLL
{
public class TodoItemException:Exception
{
public TodoItemException() { }

public TodoItemException(string message)
: base(message) { }

public TodoItemException(string message, Exception inner)
: base(message, inner) { }
}
}
56 changes: 18 additions & 38 deletions Controllers/TodoItemsController.cs
Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TodoApi.Models;
using TodoApiDTO.BLL;
using TodoApiDTO.BLL.Services;

namespace TodoApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
private readonly ITodoItemService todoItemService;

public TodoItemsController(TodoContext context)
public TodoItemsController(ITodoItemService todoItemService)
{
_context = context;
this.todoItemService = todoItemService;
}

[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
var todoItems = await todoItemService.GetTodoItemsAsync();
return Ok(todoItems);
}

[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
var todoItem = await todoItemService.GetTodoItemAsync(id);

if (todoItem == null)
{
return NotFound();
}

return ItemToDTO(todoItem);
return todoItem;
}

[HttpPut("{id}")]
Expand All @@ -47,20 +47,18 @@ public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO
return BadRequest();
}

var todoItem = await _context.TodoItems.FindAsync(id);
var todoItem = await todoItemService.GetTodoItemAsync(id);

if (todoItem == null)
{
return NotFound();
}

todoItem.Name = todoItemDTO.Name;
todoItem.IsComplete = todoItemDTO.IsComplete;

try
{
await _context.SaveChangesAsync();
await todoItemService.EditAsync(todoItemDTO);
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
catch (TodoItemException ex)
{
return NotFound();
}
Expand All @@ -71,46 +69,28 @@ public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoItemDTO.IsComplete,
Name = todoItemDTO.Name
};

_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
var todoItem = await todoItemService.AddAsync(todoItemDTO);

return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
todoItem);
}

[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
var todoItem = await todoItemService.GetTodoItemAsync(id);

if (todoItem == null)
{
return NotFound();
}

_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
await todoItemService.DeleteAsync(id);

return NoContent();
}

private bool TodoItemExists(long id) =>
_context.TodoItems.Any(e => e.Id == id);

private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
}
4 changes: 3 additions & 1 deletion Models/TodoContext.cs → DAL/DataContexts/TodoContext.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using Microsoft.EntityFrameworkCore;
using TodoApiDTO.DAL.Entities;

namespace TodoApi.Models
namespace TodoApiDTO.DAL.DataContexts
{
public class TodoContext : DbContext
{
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
{
Database.EnsureCreated();
}

public DbSet<TodoItem> TodoItems { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion Models/TodoItem.cs → DAL/Entities/TodoItem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace TodoApi.Models
namespace TodoApiDTO.DAL.Entities
{
#region snippet
public class TodoItem
Expand Down
18 changes: 18 additions & 0 deletions DAL/Repositories/ITodoItemRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TodoApi.Models;
using TodoApiDTO.DAL.Entities;

namespace TodoApiDTO.DAL.Repositories
{
public interface ITodoItemRepository
{
Task<ICollection<TodoItem>> GetTodoItemsAsync();
Task<TodoItem> GetTodoItemByIdAsync(long id);
Task InsertTodoItemAsync(TodoItem todoItem);
Task DeleteTodoItemAsync(long todoItemID);
Task UpdateTodoItemAsync(TodoItemDTO todoItemDTO);
Task SaveAsync();
}
}
63 changes: 63 additions & 0 deletions DAL/Repositories/Implementations/TodoItemRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System;
using System.Linq;
using System.Threading.Tasks;
using TodoApiDTO.DAL.DataContexts;
using TodoApiDTO.DAL.Entities;
using TodoApi.Models;

namespace TodoApiDTO.DAL.Repositories.Implementations
{

public class TodoItemRepository : ITodoItemRepository
{
private readonly TodoContext context;

public TodoItemRepository(TodoContext context)
{
this.context = context;
}

public async Task<ICollection<TodoItem>> GetTodoItemsAsync()
{
return await context.TodoItems.ToListAsync();
}

public async Task<TodoItem> GetTodoItemByIdAsync(long id)
{

return await context.TodoItems.FindAsync(id);
}

public async Task InsertTodoItemAsync(TodoItem todoItem)
{
await context.TodoItems.AddAsync(todoItem);
}

public async Task DeleteTodoItemAsync(long todoItemID)
{
TodoItem todoItem = await context.TodoItems.FindAsync(todoItemID);
if (todoItem != null)
{
context.TodoItems.Remove(todoItem);
}
}

public async Task UpdateTodoItemAsync(TodoItemDTO todoItemDTO)
{
var todoItem = await GetTodoItemByIdAsync(todoItemDTO.Id);
if (todoItem != null)
{
todoItem.Name = todoItemDTO.Name;
todoItem.IsComplete = todoItemDTO.IsComplete;
}
}

public async Task SaveAsync()
{
await context.SaveChangesAsync();
}

}
}
34 changes: 34 additions & 0 deletions GlobalErrorHandling/Extensions/ExceptionMiddlewareExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using TodoApiDTO.GlobalErrorHandling.Models;
using ILogger = Serilog.ILogger;

namespace TodoApiDTO.GlobalErrorHandling.Extensions
{
public static class ExceptionMiddlewareExtensions
{
public static void ConfigureExceptionHandler(this IApplicationBuilder app, ILogger logger)
{
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
if (contextFeature != null)
{
logger.Error($"Something went wrong: {contextFeature.Error}");
await context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = "Internal Server Error."
}.ToString());
}
});
});
}
}
}
14 changes: 14 additions & 0 deletions GlobalErrorHandling/Models/ErrorDetails.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Text.Json;

namespace TodoApiDTO.GlobalErrorHandling.Models
{
public class ErrorDetails
{
public int StatusCode { get; set; }
public string Message { get; set; }
public override string ToString()
{
return JsonSerializer.Serialize(this);
}
}
}
Loading