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
48 changes: 27 additions & 21 deletions Controllers/TodoItemsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,40 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TodoApi.Models;
using TodoApiDTO.Models;
using TodoApiDTO.Repositories;
using Microsoft.Extensions.Logging;

using TodoApiDTO.DTO;

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

public TodoItemsController(TodoContext context)
public TodoItemsController(ILoggerFactory loggerFactory,
ITodoRepository repository)
{
_context = context;
_logger = loggerFactory.CreateLogger("FileLogger");
_repository = repository;
}

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

return todoItems.Select(x => ItemToDTO(x)).ToArray();
}

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

if (todoItem == null)
{
Expand All @@ -47,7 +54,7 @@ public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO
return BadRequest();
}

var todoItem = await _context.TodoItems.FindAsync(id);
var todoItem = await _repository.GetTodoItemAsync(id);
if (todoItem == null)
{
return NotFound();
Expand All @@ -58,10 +65,12 @@ public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO

try
{
await _context.SaveChangesAsync();
await _repository.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
catch (DbUpdateConcurrencyException) when (!_repository.TodoItemExists(id))
{
_logger.LogError($"Item not found by id {id}");

return NotFound();
}

Expand All @@ -77,8 +86,8 @@ public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItem
Name = todoItemDTO.Name
};

_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
_repository.CreateTodoItem(todoItem);
await _repository.SaveChangesAsync();

return CreatedAtAction(
nameof(GetTodoItem),
Expand All @@ -89,28 +98,25 @@ public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItem
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
var todoItem = await _repository.GetTodoItemAsync(id);

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

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

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
};
};
}
}
}
3 changes: 2 additions & 1 deletion Models/TodoContext.cs → DL/TodoContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore;
using TodoApiDTO.Models;

namespace TodoApi.Models
namespace TodoApiDTO.Data
{
public class TodoContext : DbContext
{
Expand Down
4 changes: 2 additions & 2 deletions Models/TodoItemDTO.cs → DTO/TodoItemDTO.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace TodoApi.Models
namespace TodoApiDTO.DTO
{
#region snippet
public class TodoItemDTO
Expand All @@ -8,4 +8,4 @@ public class TodoItemDTO
public bool IsComplete { get; set; }
}
#endregion
}
}
38 changes: 38 additions & 0 deletions Logger/FileLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Microsoft.Extensions.Logging;
using System;
using System.IO;

namespace TodoApiDTO.Logger
{
public class FileLogger : ILogger
{
private string _path;
private static object _lock = new object();

public FileLogger(string path)
{
_path = path;
}

public IDisposable BeginScope<TState>(TState state)
{
return null;
}

public bool IsEnabled(LogLevel logLevel)
{
return logLevel == LogLevel.Error;
}

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (IsEnabled(logLevel) && formatter != null)
{
lock (_lock)
{
File.AppendAllText(_path, formatter(state, exception) + Environment.NewLine);
}
}
}
}
}
14 changes: 14 additions & 0 deletions Logger/FileLoggerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.Extensions.Logging;

namespace TodoApiDTO.Logger
{
public static class FileLoggerExtensions
{
public static ILoggerFactory AddFile(this ILoggerFactory factory,
string filePath)
{
factory.AddProvider(new FileLoggerProvider(filePath));
return factory;
}
}
}
22 changes: 22 additions & 0 deletions Logger/FileLoggerProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Microsoft.Extensions.Logging;

namespace TodoApiDTO.Logger
{
public class FileLoggerProvider : ILoggerProvider
{
private string _path;
public FileLoggerProvider(string path)
{
_path = path;
}

public ILogger CreateLogger(string categoryName)
{
return new FileLogger(_path);
}

public void Dispose()
{
}
}
}
4 changes: 1 addition & 3 deletions Models/TodoItem.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
namespace TodoApi.Models
namespace TodoApiDTO.Models
{
#region snippet
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
public string Secret { get; set; }
}
#endregion
}
16 changes: 16 additions & 0 deletions Repo/ITodoRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using TodoApiDTO.Models;

namespace TodoApiDTO.Repositories
{
public interface ITodoRepository
{
Task<IEnumerable<TodoItem>> GetTodoItemsAsync();
Task<TodoItem> GetTodoItemAsync(long id);
void CreateTodoItem(TodoItem todoItem);
void DeleteTodoItem(TodoItem todoItem);
Task SaveChangesAsync();
bool TodoItemExists(long id);
}
}
47 changes: 47 additions & 0 deletions Repo/TodoRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using TodoApiDTO.Models;
using TodoApiDTO.Data;

namespace TodoApiDTO.Repositories
{
public class TodoRepository : ITodoRepository
{
private readonly TodoContext _context;

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

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

public async Task<TodoItem> GetTodoItemAsync(long id)
{
return await _context.TodoItems.FindAsync(id);
}

public void CreateTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
}

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

public async Task SaveChangesAsync()
{
await _context.SaveChangesAsync();
}

public bool TodoItemExists(long id) =>
_context.TodoItems.Any(e => e.Id == id);
}
}
32 changes: 28 additions & 4 deletions Startup.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
Expand All @@ -11,7 +12,10 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using TodoApi.Models;
using Microsoft.OpenApi.Models;
using TodoApiDTO.Data;
using TodoApiDTO.Logger;
using TodoApiDTO.Repositories;

namespace TodoApi
{
Expand All @@ -27,17 +31,37 @@ public Startup(IConfiguration configuration)
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//services.AddDbContext<TodoContext>(opt =>
// opt.UseInMemoryDatabase("TodoList"));

services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
opt.UseSqlServer(Configuration.GetConnectionString("TodoList")));

services.AddScoped<ITodoRepository, TodoRepository>();

services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Todo API", Version = "v1" });
});
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddFile(Path.Combine(Directory.GetCurrentDirectory(), "logs", "logger.txt"));

if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Todo API v1");

// To serve SwaggerUI at application's root page, set the RoutePrefix property to an empty string.
c.RoutePrefix = string.Empty;
});
}

app.UseHttpsRedirection();
Expand All @@ -52,4 +76,4 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
});
}
}
}
}
3 changes: 3 additions & 0 deletions appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ConnectionStrings": {
"TodoList": "Server=(localdb)\\mssqllocaldb;Database=TodoList;Trusted_Connection=True;"
}
}