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
8 changes: 8 additions & 0 deletions BLL/Exceptions/EntityNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System;

namespace TodoApiDTO.BLL.Exceptions
{
public class EntityNotFoundException : Exception
{
}
}
16 changes: 16 additions & 0 deletions BLL/ITodoItemManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using TodoApiDTO.Models;

namespace TodoApiDTO.BLL
{
public interface ITodoItemManager
{
Task<ICollection<TodoItemDTO>> GetAllTodoItems();
Task<TodoItemDTO> GetTodoItemById(long id);
Task UpdateTodoItem(long id, TodoItemDTO newTodoItem);
Task<TodoItemDTO> Create(TodoItemDTO todoItemDto);
Task DeleteTodoItem(long id);
}
}
82 changes: 82 additions & 0 deletions BLL/TodoItemManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TodoApi.Models;
using TodoApiDTO.BLL.Exceptions;
using TodoApiDTO.DAL.Repositories;
using TodoApiDTO.Models;

namespace TodoApiDTO.BLL
{
public class TodoItemManager : ITodoItemManager
{
private readonly ITodoItemRepository _repository;

public TodoItemManager(ITodoItemRepository repository)
{
_repository = repository;
}

public async Task<ICollection<TodoItemDTO>> GetAllTodoItems()
{
var items = await _repository.GetAll();
return items.Select(ItemToDTO).ToList();
}

public async Task<TodoItemDTO> GetTodoItemById(long id)
{
var item = await _repository.GetById(id);
if (item == null)
{
throw new EntityNotFoundException();
}

return ItemToDTO(item);
}

public async Task UpdateTodoItem(long id, TodoItemDTO newTodoItem)
{
var todoItem = await _repository.GetById(id);
if (todoItem == null)
{
throw new EntityNotFoundException();
}

todoItem.Name = newTodoItem.Name;
todoItem.IsComplete = newTodoItem.IsComplete;

await _repository.SaveAsync();
}

public async Task<TodoItemDTO> Create(TodoItemDTO todoItemDto)
{
var todoItem = new TodoItem
{
IsComplete = todoItemDto.IsComplete,
Name = todoItemDto.Name
};
await _repository.Create(todoItem);
await _repository.SaveAsync();
return ItemToDTO(todoItem);
}

public async Task DeleteTodoItem(long id)
{
var item = await _repository.GetById(id);
if (item == null)
{
throw new EntityNotFoundException();
}
_repository.Delete(item);
await _repository.SaveAsync();
}

private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
}
78 changes: 13 additions & 65 deletions Controllers/TodoItemsController.cs
Original file line number Diff line number Diff line change
@@ -1,42 +1,33 @@
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.Models;

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

public TodoItemsController(TodoContext context)
public TodoItemsController(ITodoItemManager todoManager)
{
_context = context;
_todoManager = todoManager;
}

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

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

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

return ItemToDTO(todoItem);
var todoItem = await _todoManager.GetTodoItemById(id);
return Ok(todoItem);
}

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

var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}

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

try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
await _todoManager.UpdateTodoItem(id, todoItemDTO);

return NoContent();
}

[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 createdTodoItem = await _todoManager.Create(todoItemDTO);

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

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

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

_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();

await _todoManager.DeleteTodoItem(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
};
}
}
16 changes: 16 additions & 0 deletions DAL/Repositories/ITodoItemRepository.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;

namespace TodoApiDTO.DAL.Repositories
{
public interface ITodoItemRepository
{
public Task<List<TodoItem>> GetAll();
public Task<TodoItem> GetById(long todoItemId);
public Task Create(TodoItem newItem);

public void Delete(TodoItem item);
Task SaveAsync();
}
}
42 changes: 42 additions & 0 deletions DAL/Repositories/TodoItemRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
using TodoApi.Models;

namespace TodoApiDTO.DAL.Repositories
{
public class TodoItemRepository : ITodoItemRepository
{
private readonly TodoContext _context;

public TodoItemRepository(TodoContext context)
{
_context = context;
}

public async Task<List<TodoItem>> GetAll()
{
return await _context.TodoItems.ToListAsync();
}

public async Task<TodoItem> GetById(long todoItemId)
{
return await _context.TodoItems.FirstOrDefaultAsync(x => x.Id == todoItemId);
}

public async Task Create(TodoItem todoItem)
{
await _context.TodoItems.AddAsync(todoItem);
}

public void Delete(TodoItem todoItem)
{
_context.TodoItems.Remove(todoItem);
}

public async Task SaveAsync()
{
await _context.SaveChangesAsync();
}
}
}
3 changes: 2 additions & 1 deletion Models/TodoContext.cs → DAL/TodoContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi.Models
namespace TodoApiDTO.DAL
{
public class TodoContext : DbContext
{
Expand Down
46 changes: 46 additions & 0 deletions ErrorHandlerMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Threading.Tasks;
using System;
using TodoApiDTO.BLL.Exceptions;

namespace TodoApiDTO
{
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ErrorHandlerMiddleware> _logger;

public ErrorHandlerMiddleware(RequestDelegate next, ILogger<ErrorHandlerMiddleware> logger)
{
_next = next;
_logger = logger;
}

public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception error)
{
_logger.LogError(error, string.Empty);
var response = context.Response;
if (error is EntityNotFoundException)
{
response.StatusCode = (int)HttpStatusCode.NotFound;
}
else
{
response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
}
}
}
}
2 changes: 1 addition & 1 deletion Models/TodoItemDTO.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace TodoApi.Models
namespace TodoApiDTO.Models
{
#region snippet
public class TodoItemDTO
Expand Down
13 changes: 12 additions & 1 deletion Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,32 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Web;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;

namespace TodoApi
{
public class Program
{
public static void Main(string[] args)
{
LogManager.Setup().LoadConfigurationFromAppSettings();
CreateHostBuilder(args).Build().Run();
LogManager.Shutdown();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(LogLevel.Information);
}
).UseNLog();
}
}
Loading