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;
}
Related
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)*
I appreciate this question has been asked. But I seem to have done what people are suggesting to fix it and I am still having the same issue. I can't see where I am going wrong. Any help would be greatly appreciated.
I am getting the following error:
InvalidOperationException: A second operation started on this context
before a previous operation completed. This is usually caused by
different threads using the same instance of DbContext
In these specific areas:
return await _dbContext.Set<T>().FindAsync(id);
var editPost = await _postRepository.GetByIdAsync(postModel.Id);
await _postAppService.Update(mapped);
await _postPageService.UpdatePost(Post);
Here is my code:
Just to add a bit more detail. I will list the exact steps:
First I select the post I would like to edit. The following gets called:
public async Task<IActionResult> OnGetAsync(int? postId)
{
if (postId == null)
{
return NotFound();
}
Post = await _postPageService.GetPostById(postId.Value);
if (Post == null)
{
return NotFound();
}
return Page();
}
public async Task<PostViewModel> GetPostById(int postId)
{
var post = await _postAppService.GetPostById(postId);
var mapped = _mapper.Map<PostViewModel>(post);
return mapped;
}
public async Task<PostModel> GetPostById(int postId)
{
var post = await _postRepository.GetByIdAsync(postId);
var mapped = ObjectMapper.Mapper.Map<PostModel>(post);
return mapped;
}
public virtual async Task<T> GetByIdAsync(int id)
{
return await _dbContext.Set<T>().FindAsync(id);
}
I then make my changes and click the update button:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
try
{
await _postPageService.UpdatePost(Post);
}
catch (DbUpdateConcurrencyException)
{
if (!PostExists(Post.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
public async Task UpdatePost(PostViewModel postViewModel)
{
var mapped = _mapper.Map<PostModel>(postViewModel);
if (mapped == null)
throw new Exception($"Entity could not be mapped.");
await _postAppService.Update(mapped);
_logger.LogInformation($"Entity successfully added - IndexPageService");
}
public async Task Update(PostModel postModel)
{
ValidatePostIfNotExist(postModel);
var editPost = await _postRepository.GetByIdAsync(postModel.Id);
if (editPost == null)
throw new ApplicationException($"Entity could not be loaded.");
ObjectMapper.Mapper.Map<PostModel, Post>(postModel, editPost);
await _postRepository.UpdateAsync(editPost);
_logger.LogInformation($"Entity successfully updated - AspnetRunAppService");
}
public async Task UpdateAsync(T entity)
{
_dbContext.Entry(entity).State = EntityState.Modified;
await _dbContext.SaveChangesAsync();
}
I have also set the lifetime of my DbContext to Transient.
services.AddDbContext<MyJourneyContext>(c => c.UseSqlServer(_config.GetConnectionString("MyJourneyConnectionString")),
ServiceLifetime.Transient);
So I am using await and have set the lifetime of my DbContext to Transient. But I am clearly still doing something wrong. Can anyone help me with this?
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!
I'm trying to figure out a way to return an object in my response while still maintaining an understandable return type.
So for starters, I know this works as expected.
public async Task<HttpResponseMessage> DoMyThing(MyObject myObject)
{
var result = await _myService.CreateMyThingAsync(myObject);
return Request.CreateResponse(HttpStatusCode.Created, result);
}
But what I really want is for this pseudo code to work... somehow.
public Task<MyObject> DoMyThing(MyObject myObject)
{
var result = _myService.CreateMyThingAsync(myObject);
return Request.CreateResponse<Task<MyObject>>(HttpStatusCode.Created, result);
// or better yet
return Request.CreateResponse<MyObject>(HttpStatusCode.Created, result);
}
Is there any magic in the framework that'll make this happen? Or are there any third party libraries that can do this?
Essentially I need to return the Task<MyObject> instead of the Task<HttpResponseMessage>
I'm also open to other suggestions on how to return a non 200 response while still returning the Task<MyObject>
The issue with specifying the type as the return type is that you restrict yourself to having to return that type. That may sound strange but actually there will be many cases where you need to be able to support multiple response, such as 404, 200 201 and so on.
To handle the documentation of this you can use the ResponseType attribute, like so:
[ResponseType(typeof(BookDto))]
public async Task<IHttpActionResult> GetBook(int id)
{
BookDto book = await db.Books.Include(b => b.Author)
.Where(b => b.BookId == id)
.Select(AsBookDto)
.FirstOrDefaultAsync();
if (book == null)
{
return NotFound();
}
return Ok(book);
}
Take a look here
Edit:
In Asp.Net Core you use the ProducesResponseType attribute which can be used multiple times per method
See here
Example
[ProducesResponseType(typeof(BookDto), 200)]
[ProducesResponseType(typeof(object), 201)]
public async Task<IActionResult> GetBook(int id)
{
BookDto book = await db.Books.Include(b => b.Author)
.Where(b => b.BookId == id)
.Select(AsBookDto)
.FirstOrDefaultAsync();
if (book == null)
{
return NotFound();
}
return Ok(book);
}
EDIT: Multiple response attributes prior to dot net core
You can use Swagger to help document / describe your API, they have a custom attribute called SwaggerResponse
The .Net port of Swagger is Swashbuckle, take a look here
This would be the best pattern in WebApi.
public async Task<IHttpActionResult> DoMyThing(MyObject myObject)
{
try
{
var result = await _myService.CreateMyThingAsync(myObject);
return new JsonStreamHttpActionResult<MyObject>(Request, System.Net.HttpStatusCode.Created, result);
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
return new HttpActionResult(HttpStatusCode.InternalServerError, "An error has occured");
}
}
with a generic serializer. You can then use the "better" IHttpActionResult instead of a real return value.
public class JsonStreamHttpActionResult<T> : IHttpActionResult
{
private T responseData;
private HttpRequestMessage request;
private HttpStatusCode statusCode;
public JsonStreamHttpActionResult(HttpRequestMessage request, System.Net.HttpStatusCode code, T responseData)
{
this.responseData = responseData;
this.request = request;
this.statusCode = code;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = request.CreateResponse(statusCode);
response.Content =
new PushStreamContent((stream, content, context) =>
{
var serializer = new Newtonsoft.Json.JsonSerializer();
using (var writer = new System.IO.StreamWriter(stream))
{
serializer.Serialize(writer, responseData);
stream.Flush();
}
});
return Task.FromResult(response);
}
}
I have an ASP.NET Core 1.0 RC based Application using an async EF6 generic base repository which has a GetAsync method:
public virtual async Task<T> GetAsync(Expression<Func<T, bool>> predicate, bool includeSoftDeleted = false)
{
try
{
return
await
_context.Set<T>().Where(e => includeSoftDeleted || !e.IsDeleted).FirstOrDefaultAsync(predicate);
}
catch (Exception ex)
{
_logger.LogError($"Unable to get the {typeof(T).Name}. See the exception for more details.",predicate,ex);
return null;
}
}
All of my repositories use this class as base class and are injected with Autofac as InstancePerLifetime() in my services.
So I have a method in my service that calls this GetAsync:
public async Task<int?> GetRoleAccessPermissionLevelAsync(UserRole userRole, Access access)
{
// TODO: Implement IEquatable for Access to replace with a == access
var accessFromDb = await _accessRepository.GetAsync(a => a.Module == access.Module && a.Controller == access.Controller && a.Action == access.Action);
if(accessFromDb == null) return null;
var roleAccess = await _roleAccessRepository.GetAsync(r =>
r.RoleId == userRole.RoleId && r.AccessId == accessFromDb.Id);
return (int?)roleAccess?.AccessLevel;
}
Here, the accessFromDb is sometimes null, sometimes not, with the same values given to it: access.Module = "Main", Controller and action are null.
How is it possible that, without changing the context or the repository's Set<T>(), that this is sometimes null and sometimes returns the object?
I assume this has something to do with a 'lock' that I have to put somewhere for the async?