How to retrieve a message from a repository with C# WebApi - c#

I'm still learning how to use properly Dependencies with C# and WebApi and I came up with a problem. I have a repository where I register a user but since is a async task void method it doesn't return nothing. So the question is, what is the best way to do this? It would be with Task<T> and handling the result in the Controller ?
The Classes :
public interface IGeneral
{
Task RegisterAsync(UserModel model);
}
public class General : BaseRepository, IGeneral
{
public General(Context context) : base(context)
{
}
public async Task RegisterAsync(UserModel model)
{
var result = await Context.User.Where(a => a.Email == model.Email).FirstOrDefaultAsync();
if(result != null)
{
await Context.User.AddAsync(new Data.Access.Models.User
{ Date = DateTime.Now, Email = model.Email, Name = model.Name, Password = model.Password });
await Context.SaveChangesAsync();
}
}
}
public abstract class BaseRepository
{
protected readonly Context Context;
public BaseRepository(Context context)
{
Context = context;
}
}

The possible solution that you have suggested in your question - would be the approach I would use.
public async Task<bool> RegisterAsync(UserModel model)
{
bool operationResult = false;
var result = await Context.User.Where(a => a.Email == model.Email).FirstOrDefaultAsync();
if (result != null)
{
await Context.User.AddAsync(new Data.Access.Models.User
{ Date = DateTime.Now, Email = model.Email, Name = model.Name, Password = model.Password });
if(await Context.SaveChangesAsync() > 0)
{
operationResult = true;
}
}
return operationResult;
}
From MSDN documentation we know that SaveChangesAsync() signature
public virtual System.Threading.Tasks.Task<int> SaveChangesAsync ();
returns an int value.
Checking for a result greater than zero assert that changes have occurred and they have being made persistent.

You should not use Task<T> return type of RegisterAsync(UserModel model) method to handle result in the controller because
you have to handle exception in RegisterAsync method and you can return bool return type in result but in production, you have to write log for these exceptions when you catch it.
It's not a good way to do this in repository methods.
Repostiory should have a single responsibility.
You can keep this method as it is and can use try catch block in your services or in controller appropriately, depends on what you want to send at the time of exception by considering types of clients(Users)*

Related

How to correctly bubble up exceptions from Tasks

I'm trying to write an async method which reads an item from a database and provides some amount of validation before and after the read. The happy path works no problem, but I'm having trouble working out how to correctly throw exceptions.
Here's my code:
protected override async Task<Entity> InternalRead<TEntity>(object id)
{
var result = this.context.Set<Entity>().FindAsync(id);
return await result;
}
public Task<TEntity> Read<Entity>(object id) where TEntity : class
{
return InternalRead<Entity>(id)
.ContinueWith(entityTask =>
{
var entity = entityTask.Result;
if (entity != null && !entity.IsHidden)
throw new UnauthorizedAccessException();
return entity;
});
}
[Fact]
public async Task InvalidIdThrowsExpectedException()
{
var db = *getDBCode*
var identity = new Identity();
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => accessor.Read<TradingStyle>(1, identity));
}
The Entity with id 1 is hidden, and when I step through the code I can see that the exception is thrown as expected, but the test is seeing AggregateException being thrown and not UnauthorizedAccessException. I can't see what's different between my setup and the examples I've been reading, but I'm confused why Assert.ThrowAsync isn't unwrapping the internal exception.
Don't use ContinueWith; use await instead. This is a good general rule for most async code. ContinueWith is a low-level method with surprising default behavior; await works the way you expect it to:
public async Task<TEntity> Read<Entity>(object id) where TEntity : class
{
var entity = await InternalRead<Entity>(id);
if (entity != null && !entity.IsHidden)
throw new UnauthorizedAccessException();
return entity;
}

testing Api's firebase and xamarion form

I'm trying to write Api's using xamarion and do crud Operation in firebase real time database, But this is my first time to deal with it now i want to test this api's without need to write xaml code and create form for example, How can I do it ? in node js and web development we use postman, here what we can do ?
this is my code for example :
public async Task<bool> Save(Specalist specalist)
{
var data = await firebaseClient.Child(nameof(Specalist)).PostAsync(JsonConvert.SerializeObject(specalist));
if (string.IsNullOrEmpty(data.Key))
{
return true;
}
return false;
}
public async Task<List<Specalist>> GetAll()
{
return (await firebaseClient.Child(nameof(Specalist)).OnceAsync<Specalist>()).Select(item => new Specalist
{
Email = item.Object.Email,
Name = item.Object.Name,
ID = item.Object.ID
}).ToList();
}
public async Task<bool> Update(Specalist specalist)
{
await firebaseClient.Child(nameof(Specalist)+"/"+specalist.ID).PatchAsync(JsonConvert.SerializeObject(specalist));
return true;
}
public async Task<bool> Delete(string id)
{
await firebaseClient.Child(nameof(Specalist)+"/"+id).DeleteAsync();
return true;
}

MediatR CQRS - How to deal with unexisting resources (asp.net core web api)

So I've recently started to learn about using the MediatR library with ASP.NET Core Web API and I'm unsure how to go about returning a NotFound() when a DELETE/PUT/PATCH request has been made for an unexisting resource.
If we take DELETE for example, here is my controller action:
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
await Mediator.Send(new DeleteCourseCommand {Id = id});
return NoContent();
}
The Command:
public class DeleteCourseCommand : IRequest
{
public int Id { get; set; }
}
The Command Handler:
public class DeleteCourseCommandHandler : IRequestHandler<DeleteCourseCommand>
{
private readonly UniversityDbContext _context;
public DeleteCourseCommandHandler(UniversityDbContext context)
{
_context = context;
}
public async Task<Unit> Handle(DeleteCourseCommand request, CancellationToken cancellationToken)
{
var course = await _context.Courses.FirstOrDefaultAsync(c => c.Id == request.Id, cancellationToken);
if (course != null)
{
_context.Courses.Remove(course);
var saveResult = await _context.SaveChangesAsync(cancellationToken);
if (saveResult <= 0)
{
throw new DeleteFailureException(nameof(course), request.Id, "Database save was not successful.");
}
}
return Unit.Value;
}
}
As you can see in the Handle method, if there is an error when saving, an exception is thrown which results in a 500 internal server error (which is correct I believe). But if the Course is not found, how can I feed this back to the Action on the Controller? Is it simply a case of invoking a Query to GET the course in the Controller Action, then return NotFound() if it doesn't exist or then invoke the Command to DELETE the Course? This would work of course but of all the examples I've been through, I haven't come across an Action which uses two Mediator calls.
MediatR supports a Request/Response pattern, which allows you to return a response from your handler class. To use this approach, you can use the generic version of IRequest, like this:
public class DeleteCourseCommand : IRequest<bool>
...
In this case, we're stating that bool will be the response type. I'm using bool here for simplicity: I'd suggest using something more descriptive for your final implementation but bool suffices for explanation purposes.
Next, you can update your DeleteCourseCommandHandler to use this new response type, like this:
public class DeleteCourseCommandHandler : IRequestHandler<DeleteCourseCommand, bool>
{
...
public async Task<bool> Handle(DeleteCourseCommand request, CancellationToken cancellationToken)
{
var course = ...
if (course == null)
return false; // Simple example, where false means it wasn't found.
...
return true;
}
}
The IRequestHandler being implemented now has two generic types, the command and the response. This requires updating the signature of Handle to return a bool instead of Unit (in your question, Unit isn't being used).
Finally, you'll need to update your Delete action to use the new response type, like this:
public async Task<IActionResult> Delete(int id)
{
var courseWasFound = await Mediator.Send(new DeleteCourseCommand {Id = id});
if (!courseWasFound)
return NotFound();
return NoContent();
}
I like returning events from my commands. The command is telling your application what the client wants it to do. The response is what it actually did.
BTW—it's said that command handlers should return anything. That's really only true in a fully async environment where the command won't be completed until sometime after the response to the client that it's accepted. In that case, you would return Task<Unit> and publish these events. The client would get them via some other channel, like a SignalR hub once they were raised. Either way, events are the best way to tell a client what's going on in your application.
Start by defining an interface for your events
public interface IEvent
{
}
Then, create events for each of the things that can happen in a command. You can include information in them if you'd want to do something with that information or just leave them empty if the class itself is enough.
public class CourseNotFoundEvent : IEvent
{
}
public class CourseDeletedEvent : IEvent
{
}
Now, have your command return an event interface.
public class DeleteCourseCommand : IRequest<IEvent>
{
}
Your handler would look something like this:
public class DeleteCourseCommandHandler : IRequestHandler<DeleteCourseCommand, IEvent>
{
private readonly UniversityDbContext _context;
public DeleteCourseCommandHandler(UniversityDbContext context)
{
_context = context;
}
public async Task<IEvent> Handle(DeleteCourseCommand request, CancellationToken cancellationToken)
{
var course = await _context.Courses.FirstOrDefaultAsync(c => c.Id == request.Id, cancellationToken);
if (course is null)
return new CourseNotFoundEvent();
_context.Courses.Remove(course);
var saveResult = await _context.SaveChangesAsync(cancellationToken);
if (saveResult <= 0)
{
throw new DeleteFailureException(nameof(course), request.Id, "Database save was not successful.");
}
return new CourseDeletedEvent();
}
}
Finally, you can use pattern matching on your web API to do things based on the event that gets returned.
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var #event = await Mediator.Send(new DeleteCourseCommand {Id = id});
if(#event is CourseNotFoundEvent)
return NotFound();
return NoContent();
}
I managed to solve my problem through some more examples I found. The solution is to define custom Exceptions such as NotFoundException and then throw this in the Handle method of the Query/Command Handler. Then in order for MVC to handle this appropriately, an implementation of ExceptionFilterAttribute is needed to decide how each Exception is handled:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
if (context.Exception is ValidationException)
{
context.HttpContext.Response.ContentType = "application/json";
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
context.Result = new JsonResult(
((ValidationException)context.Exception).Failures);
return;
}
var code = HttpStatusCode.InternalServerError;
if (context.Exception is NotFoundException)
{
code = HttpStatusCode.NotFound;
}
context.HttpContext.Response.ContentType = "application/json";
context.HttpContext.Response.StatusCode = (int)code;
context.Result = new JsonResult(new
{
error = new[] { context.Exception.Message }
});
}
}
Startup Class:
services.AddMvc(options => options.Filters.Add(typeof(CustomExceptionFilterAttribute)));
Custom Exception:
public class NotFoundException : Exception
{
public NotFoundException(string entityName, int key)
: base($"Entity {entityName} with primary key {key} was not found.")
{
}
}
Then in the Handle method:
if (course != null)
{
_context.Courses.Remove(course);
var saveResult = await _context.SaveChangesAsync(cancellationToken);
if (saveResult <= 0)
{
throw new DeleteFailureException(nameof(course), request.Id, "Database save was not successful.");
}
}
else
{
throw new NotFoundException(nameof(Course), request.Id);
}
return Unit.Value;
This seems to do the trick, if anyone can see any potential issues with this please let me know!

How to avoid not-safe context operations in EF Core? [duplicate]

This question already has answers here:
Entity Framework Core: A second operation started on this context before a previous operation completed
(20 answers)
Closed 4 years ago.
I'd want to know how why creating instances of other classes with current database context instances as a parameter and using that db context causes this exception to be raised
'A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.'
Imma use this sample code to show the problem
public class TestController : Controller
{
private readonly DbContext dbContext;
public Controller(DbContext ctx)
{
dbContext = ctx;
}
public async Task<IActionResult> Test(string id)
{
var isValid = new otherClass(dbContext).Validate(id);
if (!isValid)
{
return View("error");
}
var user = dbContext.Users.FirstOrDefault(x => x.Id == id);
user.Age++;
dbContext.SaveChanges(); // exception is being raised here. It is second .SaveChanges() here
return View();
}
}
public class otherClass
{
private readonly DbContext dbContext;
public otherClass(DbContext ctx)
{
dbContext = ctx;
}
public bool Validate(string id)
{
var user = dbContext.Users.FirstOrDefault(x => x.Id == id);
user.ValidationAttempt = DateTime.Now;
dbContext.SaveChanges();
return user.HasConfirmedEmail;
}
}
Generally in an MVC fashion youre going to want a DbContext on a per request basis but when using threading more control through using blocks can be beneficial, an easy way to set that up would be something along the lines of
public class TestController : Controller
{
private readonly Func<DbContext> dbContext;
public Controller(Func<DbContext> ctx)
{
dbContext = ctx;
}
public async Task<IActionResult> Test(string id)
{
using(var cntx = dbContext())
{
var isValid = new otherClass(cntx).Validate(id);
if (!isValid)
{
return View("error");
}
var user = cntx.Users.FirstOrDefault(x => x.Id == id);
user.Age++;
cntx.SaveChanges();
return View();
}
}
}
that essentially resolves a new DbContext per using block - and since each thread is then handling its own DbContext - shouldnt have any issues

Unit test failing on EF Entry.State

Is it possible to unit test this?
public class MyRepository<T> where T : IdentityUser, new()
{
public async Task UpdateAsync(T user)
{
_context.Entry(user).State = EntityState.Modified;
_context.Entry(user).Property("UserName").IsModified = false;
await _context.SaveChangesAsync();
}
}
The [TestInitialize] adds 1 user to the repository
_user = new IdentityUser { Id = "70a038cdde40" };
IDbSet<IdentityUser> users = new FakeDbSet<IdentityUser> { _user };
var dbContext = new Mock<MyDbContext<IdentityUser>>();
dbContext.Setup(x => x.Users).Returns(() => users);
_repository = new MyRepository<IdentityUser>(dbContext.Object);
and I'm trying to test with this
private MyRepository<IdentityUser> _repository;
[TestMethod]
public async Task UpdateUser_Success2()
{
var user = await _repository.FindByIdAsync("70a038cdde40");
Assert.IsFalse(user.EmailConfirmed, "User.EmailConfirmed is True");
user.EmailConfirmed = true;
await _repository.UpdateAsync(user);
(...)
}
But it dies on 1st line of UpdateAsync. Is the test that is wrong or the UpdateAsync implementation? Is there any way I can test it?
Edit
I added as suggested by Belogix
dbContext.Setup(x => x.Entry(It.IsAny<IdentityUser>()))
.Returns(() => dbContext.Object.Entry(_user));
That gets me closer, I think, but still have the non-virtual error: Invalid setup on a non-virtual member: x => x.Entry(It.IsAny())
Best quote ever: "All problems in computer science can be solved by another level of indirection" - Butler Lampson.
It looks like this can't be tested directly without adding some additional abstraction. I had to refactor my UpdateAsync method this way
public async Task UpdateAsync(T user)
{
SetEntityStateModified(user);
SetPropertyIsModified(user);
await _context.SaveChangesAsync();
}
public virtual void SetPropertyIsModified(T user)
{
_context.Entry(user).Property("UserName").IsModified = false;
}
public virtual void SetEntityStateModified(T user)
{
_context.Entry(user).State = EntityState.Modified;
}
And then update my test code in the Initialize
_repository = new Mock<MyRepository<IdentityUser>>(dbContext.Object);
_repository.Setup(x => x.SetEntityStateModified(It.IsAny<IdentityUser>()));
_repository.Setup(x => x.SetPropertyIsModified(It.IsAny<IdentityUser>()));
My test then finally passes
[TestMethod]
public async Task can_update_user_details()
{
//Arrange
var user = await _repository.Object.FindByIdAsync("70a038cdde40");
Assert.IsFalse(user.EmailConfirmed, "User.EmailConfirmed is True");
//Act
user.EmailConfirmed = true;
await _repository.Object.UpdateAsync(user);
var newUser = await _repository.Object.FindByIdAsync("70a038cdde40");
//Assert
Assert.IsTrue(newUser.EmailConfirmed, "User.EmailConfirmed is False");
}
The ChangeTracker in dbContext tracks changes and hold the entities that are changed. So you can assert the changed entity is among them.
Assert.IsTrue(dbContext.Object.ChangeTracker.Entries().Any(entry =>
entry.State == EntityState.Modified &&
entry.Entity is IdentityUser &&
(entry.Entity as IdentityUser).Id == users[0].Id // Here you can check if it is actually the same user
));
For the property it would be something like this:
Assert.IsTrue(_context.Object.ChangeTracker.Entries().Any(entry =>
entry.Property("UserName").IsModified == false &&
entry.Entity is IdentityUser &&
(entry.Entity as IdentityUser).Id == users[0].Id // Here you can check if it is actually the same user
));
It looks like you have not stubbed your context correctly... I am not at a computer with Visual Studio so here is some pseudo code that should demonstrate what I mean. Replace the IsAnything with either your mocking frameworks way of ignoring argument or actually the user if you want to handle different responses.
// Existing context initialisation...
var dbContext = new Mock<MyDbContext<IdentityUser>>();
dbContext.Setup(x => x.Users).Returns(() => users);
// NEW: Mock what / how Entry is going to return when called (i.e. return a user)
dbContext.Setup(x => x.Entry(IsAnything)).Returns(() => users[0]);

Categories

Resources