refactoring towards.net core api conventions - c#

In our application we are using mediatr and there is a common pattern as follows:
class SomeController
{
public Action Foo(SomeRequest request)
{
var result = Mediatr.Send(request);
if(result == null)
{
return NotFound();
}
return Ok(result);
}
}
This code repeats for every API end point, regardless of the HTTP method.
I read about API conventions but I guess that is about Swagger, API analyser and such.
How can I avoid having this repetetive code above?

class BaseController
{
protected static IActionResult GenericAction(object request)
{
var result = Mediatr.Send(request);
if(result == null)
{
return NotFound();
}
return Ok(result);
}
}
Then
class SomeController : BaseController
{
public Action Foo(SomeRequest request)
{
return GenericAction(request);
}
}
if your methods always has same structure you can even generalize it more
class BaseController<TRequest>
{
public virtual Action Foo(SomeRequest request)
{
return GenericAction(request);
}
protected static IActionResult GenericAction(TRequest request)
{
var result = Mediatr.Send(request);
if(result == null)
{
return NotFound();
}
return Ok(result);
}
}
Then
class SomeController<SomeRequest> : BaseController
{
}

Related

how to handle Extension methods in controller?

I really Didn't get proper Title for this question. Please correct it if its misleading.
I have a WebApi controller, where there are multiple validation check are there. Controller sample code
public async Task<IActionResult> UploadFile(IFormFile file)
{
try
{
return file.IsValid();
//some more Functionality
}
}
Here Isvalid is a Extension method where the code is as follows
public static IActionResult PrepareResult(this ControllerBase controller, IFormFile file)
{
if (file== null)
{
return controller.Badrequest("No data sent");
}
return controller.Ok();
}
Issue:- In current scenario , if the file is Null then Extension method will be returning Badrequest() & the same will be returned to the client. But if file is not null then It's going to return Ok() & same will be returned to the Clint, where as i have more code to execute(i.e.//some more Functionality).
I don't want to return controller.Ok(), so that for positive scenario i can continue with my remaining code.
NB:- i don't want to assign to any variable & check with If condition. In order to avoid if condition only i am using extension methods.
Not sure why you don't want to assign varaibles and avoid if condition as this is the most efficient way. You can use exception handling, though that comes with a performance cost.
public static void EnsureFileIsValid(this IFormFile file)
{
if(file == null) { throw new InvalidOperationException("No data sent"); }
}
public async Task<IActionResult> UploadFile(IFormFile file)
{
try
{
file.EnsureFileIsValid();
return Ok();
}
catch(InvalidOperationException ex)
{
return BadRequest(ex.Message);
}
}
You can pass a action to your method like :
public static IActionResult PrepareResult(this ControllerBase controller, IFormFile file, Action<IFormFile> work)
{
if (file == null)
{
return controller.Badrequest("No data sent");
}
work(file);
return controller.Ok();
}
In you action, the use is :
public class FilesController : ControllerBase
{
[HttpPost]
public IActionResult UploadFile([FromServices]IFileService fileService, IFormFile file)
{
return Ok(PrepareResult(file, fileService.Upload));
}
}
But maybe you can consider to use validation.
In validation step, you enforce a parameter isn't null with RequiredAttribute.
ApiControllerAttribute enforce the validation before the action is called. If the validation fail, then ASP.NET Core return directly BadRequest and the action isn't called.
In this example, if the parameter file is null then the action isn't called and it return BadRequest :
[ApiController]
public class FilesController : ControllerBase
{
[HttpPost]
public IActionResult UploadFile([Required]IFormFile file)
{
//Do something
return Ok();
}
[HttpPut]
public IActionResult UploadFileBis([Required] IFormFile file)
{
//Do something
return Ok();
}
}
PS : You can use [ApiControllerAttribute] at assembly level, it will be enable to all controllers in assembly :
[assembly: ApiController]
If you want to verify conditions and define the error response in the same lower layer class, here is a solution.
First, let's create a custom exception that will hold the http status code and message to return:
// serialization implementation redacted
public class InfrastructureException : Exception
{
public HttpStatusCode HttpStatusCode { get; }
public InfrastructureException(HttpStatusCode code, string message) : base(message)
{
HttpStatusCode = code;
}
}
We need a class to handle the response serialization:
public class ExceptionResponse
{
public int StatusCode { get; set; }
public string Message { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
Then create a middleware that handle exceptions:
public class InfrastructureExceptionMiddleware
{
private readonly RequestDelegate next;
public InfrastructureExceptionMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task InvokeAsync(HttpContext httpContext, IHostEnvironment hostEnvironment)
{
try
{
await this.next(httpContext);
}
catch (Exception ex)
{
await HandleExceptionAsync(httpContext, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
ExceptionResponse response = exception is InfrastructureException infrastructureException
? new ExceptionResponse()
{
StatusCode = (int)infrastructureException.HttpStatusCode,
Message = infrastructureException.Message
}
: new ExceptionResponse()
{
StatusCode = (int)HttpStatusCode.InternalServerError,
Message = ReasonPhrases.GetReasonPhrase(context.Response.StatusCode)
};
return context.Response.WriteAsync(response.ToString());
}
}
Now, we need to register our middleware:
public class Startup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// redacted
app.UseMiddleware<InfrastructureExceptionMiddleware>();
// redacted
}
}
In the controller, we delegate the validation to the extension method:
public async Task<IActionResult> UploadFile(IFormFile file)
{
file.IsValid();
// now you can consider file is valid
}
Finally, in the extension method, we throw the exception:
public static void IsValid(this IFormFile file)
{
if(file == null)
{
throw new InfrastructureException(HttpStatusCode.BadRequest, "No data sent");
}
if(...) // condition for Http NotFound
{
throw new InfrastructureException(HttpStatusCode.NotFound, "Data not found");
}
// other validation conditions
}

Testing crud operations using xunit in an asp.net web api core application [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I am completely new to unit testing and will like to write unit test for my controller
below using xunit and moq. I understand that you can only mock database transactions and
make the unit test independent of the database.
How can I go about writing xunit tests for these? any documentation or sample code will be appreciated.
I know how to add the xnit test project to my solution.
using System;
using System.Threading.Tasks;
using CoreServices.Models;
using CoreServices.Repository;
using Microsoft.AspNetCore.Mvc;
namespace CoreServices.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class PostController : ControllerBase
{
IPostRepository postRepository;
public PostController(IPostRepository _postRepository)
{
postRepository = _postRepository;
}
[HttpGet]
[Route("GetCategories")]
public async Task<IActionResult> GetCategories()
{
try
{
var categories = await postRepository.GetCategories();
if (categories == null)
{
return NotFound();
}
return Ok(categories);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpGet]
[Route("GetPosts")]
public async Task<IActionResult> GetPosts()
{
try
{
var posts = await postRepository.GetPosts();
if (posts == null)
{
return NotFound();
}
return Ok(posts);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpGet]
[Route("GetPost")]
public async Task<IActionResult> GetPost(int? postId)
{
if (postId == null)
{
return BadRequest();
}
try
{
var post = await postRepository.GetPost(postId);
if (post == null)
{
return NotFound();
}
return Ok(post);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpPost]
[Route("AddPost")]
public async Task<IActionResult> AddPost([FromBody]Post model)
{
if (ModelState.IsValid)
{
try
{
var postId = await postRepository.AddPost(model);
if (postId > 0)
{
return Ok(postId);
}
else
{
return NotFound();
}
}
catch (Exception)
{
return BadRequest();
}
}
return BadRequest();
}
[HttpPost]
[Route("DeletePost")]
public async Task<IActionResult> DeletePost(int? postId)
{
int result = 0;
if (postId == null)
{
return BadRequest();
}
try
{
result = await postRepository.DeletePost(postId);
if (result == 0)
{
return NotFound();
}
return Ok();
}
catch (Exception)
{
return BadRequest();
}
}
[HttpPost]
[Route("UpdatePost")]
public async Task<IActionResult> UpdatePost([FromBody]Post model)
{
if (ModelState.IsValid)
{
try
{
await postRepository.UpdatePost(model);
return Ok();
}
catch (Exception ex)
{
if (ex.GetType().FullName == "Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException")
{
return NotFound();
}
return BadRequest();
}
}
return BadRequest();
}
}
}
You can just instantiate that controller with new PostController(postRepository).
If you need a mock for the post repository you can use a library called Moq. (Use nuget to get it). Simply write:
using Moq;
Mock<IPostRepository> postRepositoryMock = new Mock<IPostRepository>();
PostController controller = new PostController(postRepositoryMock.Object);
then you can use the Setup method on the Mock to return what you want. For example to Mock the Get method in a way that returns a test Post for any given Id of type int?:
postRepositoryMock.Setup(it => it.Get(It.IsAny<int?>()).Returns(new Post() { //here what you need to build your post object })
Then you can execute the code to get the result from the controller, for example (Test Code is made using NUnit, I don't use xunit but the concept is the same):
using Moq;
[TestFixture]
public class ExampleFixture()
{
[Test]
public void ExampleTest()
{
Mock<IPostRepository> postRepositoryMock = new Mock<IPostRepository>();
PostController controller = new PostController(postRepositoryMock.Object);
postRepositoryMock.Setup(it => it.Get(It.IsAny<int?>()).Returns(new Post() { //here what you need to build your post object })
var result = controller.GetPost(1);
Assert.That(result, Is.Not.Null, "Unexpected null result");
var retrievedPostContent = result as OkNegotiatedContentResult<Post>;
Assert.That(retrievedPostContent, Is.Not.Null, "Unexpected null retrievedPost");
var retrievedPost = result.Content;
Assert.That(retrievedPost.Id, Is.EqualTo(1), "retrievedPost.Id is unexpected")
}
}
with xUnit should became:
using Xunit;
using Moq;
namespace ATestNamespace
{
public class TestFixture
{
public TestFixture()
{
}
[Fact]
public void Test()
{
Mock<IPostRepository> postRepositoryMock = new Mock<IPostRepository>();
PostController controller = new PostController(postRepositoryMock.Object);
postRepositoryMock.Setup(it => it.Get(It.IsAny<int?>()).Returns(new Post() { //here what you need to build your post object });
var result = controller.GetPost(1);
Assert.True(result != null,"Unexpected null result");
var retrievedPostResult = result as OkNegotiatedContentResult<Post>;
Assert.True(retrievedPostResult != null, "Unexpected null retrievedPost");
var retrievedPost = result.Content;
Assert.True(retrievedPost.Id == 1, "retrievedPost.Id is unexpected")
}
}
}

How do I set the status code with custom message in asp.net core 2.1?

I am working on asp.net core version 2.1, I have created a sample API project which works fine but I am unable to modify the status code with a custom message for example:
In Postman:
200 OK
Expecting:
200 Custom_Message
The code that I tried:
[HttpGet]
public IActionResult Get()
{
Response.StatusCode = 200;
Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = "Custom_Message";
return Ok();
}
Postman's current Output:
GitHub Repository
I think you should create your custom class:
public class CustomResult : OkResult
{
private readonly string Reason;
public CustomResult(string reason) : base()
{
Reason = reason;
}
public override void ExecuteResult(ActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
context.HttpContext.Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = Reason;
context.HttpContext.Response.StatusCode = StatusCode;
}
}
Then in your controller method:
[HttpGet]
public IActionResult Get()
{
return new CustomResult("Custom reason phrase");
}
Output
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
public abstract class BaseApiController : ControllerBase
{
public override OkResult Ok()
{
return base.Ok();
}
public override OkObjectResult Ok([ActionResultObjectValue] object value)
{
return base.Ok(new Response
{
Result = value,
Status = ResultStatus.Success,
Message = string.Empty
});
}
}

C# WebApi refactoring Select Linq

I'm currently writing a C# Web Api in Visual Studio 2015. I'm actually copy pasting quite a lot of code.
public class APIController : ApiController
{
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = db.drones.Select(d => new DroneDTO
{
iddrones = d.iddrones,
//more stuff
});
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = db.drones.Select(d => new DroneDTO
{
iddrones = d.iddrones,
//more stuff
}).Where(drones => drones.iddrones == id);
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
}
How should I refactor that? At first I thought about moving the var to a class member, but that doesn't seem to be allowed.
I would make a DTO factory method that worked on IQueryable<T> and then the two functions would only be responsible for creating the proper query.
This will position you better in the future when you make these functions async.
public class DroneDTO
{
public int Id { get; set; }
public static IEnumerable<DroneDTO> CreateFromQuery(IQueryable<Drone> query)
{
return query.Select(r=> new DroneDTO
{
Id = r.Id
});
}
}
public class APIController : ApiController
{
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = DroneDTO.CreateFromQuery(db.drones);
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = DroneDTO.CreateFromQuery(db.drones.Where(d => d.iddrone == id));
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
}
I had the same problem about one year ago and unified the code by few steps:
At First, I separated my business logic from the controller in another classes. It's not one to one separation, I created class for each Entity. The other way is to using CQRS for each query/command. The general case that my business logic always returns one of this models:
public class OutputModel
{
[JsonIgnore]
public OperationResult Result { get; private set; }
public OutputDataModel(OperationResult result)
{
Result = result;
}
#region Initializatiors
public static OutputModel CreateResult(OperationResult result)
{
return new OutputModel(result);
}
public static OutputModel CreateSuccessResult()
{
return new OutputModel(OperationResult.Success);
}
#endregion Initializatiors
}
public class OutputDataModel<TData> : OutputModel
{
public TData Data { get; private set; }
public OutputDataModel(OperationResult result)
: base(result)
{
}
public OutputDataModel(OperationResult result, TData data)
: this(result)
{
Data = data;
}
#region Initializatiors
public static OutputDataModel<TData> CreateSuccessResult(TData data)
{
return new OutputDataModel<TData>(OperationResult.Success, data);
}
public static OutputDataModel<TData> CreateResult(OperationResult result, TData data)
{
return new OutputDataModel<TData>(result, data);
}
public new static OutputDataModel<TData> CreateResult(OperationResult result)
{
return new OutputDataModel<TData>(result);
}
#endregion Initializatiors
}
Operation result is an Enumeration that contains something like StatusCode in a platform independent style:
public enum OperationResult
{
AccessDenied,
BadRequest,
Conflict,
NotFound,
NotModified,
AccessDenied,
Created,
Success
}
It allowed me to handle all web api calls on the same manner and uses my business logic not only in web api but in other clients (for example, I created small WPF app which uses my business logic classes to display operational information).
I created base API controller that handle OutputDataModel to compose response:
public class RikropApiControllerBase : ApiController
{
#region Result handling
protected HttpResponseMessage Response(IOutputModel result, HttpStatusCode successStatusCode = HttpStatusCode.OK)
{
switch (result.Result)
{
case OperationResult.AccessDenied:
return Request.CreateResponse(HttpStatusCode.Forbidden);
case OperationResult.BadRequest:
return Request.CreateResponse(HttpStatusCode.BadRequest);
case OperationResult.Conflict:
return Request.CreateResponse(HttpStatusCode.Conflict);
case OperationResult.NotFound:
return Request.CreateResponse(HttpStatusCode.NotFound);
case OperationResult.NotModified:
return Request.CreateResponse(HttpStatusCode.NotModified);
case OperationResult.Created:
return Request.CreateResponse(HttpStatusCode.Created);
case OperationResult.Success:
return Request.CreateResponse(successStatusCode);
default:
return Request.CreateResponse(HttpStatusCode.NotImplemented);
}
}
protected HttpResponseMessage Response<TData>(IOutputDataModel<TData> result, HttpStatusCode successStatusCode = HttpStatusCode.OK)
{
switch (result.Result)
{
case OperationResult.AccessDenied:
return Request.CreateResponse(HttpStatusCode.Forbidden);
case OperationResult.BadRequest:
return Request.CreateResponse(HttpStatusCode.BadRequest);
case OperationResult.Conflict:
return Request.CreateResponse(HttpStatusCode.Conflict);
case OperationResult.NotFound:
return Request.CreateResponse(HttpStatusCode.NotFound);
case OperationResult.NotModified:
return Request.CreateResponse(HttpStatusCode.NotModified, result.Data);
case OperationResult.Created:
return Request.CreateResponse(HttpStatusCode.Created, result.Data);
case OperationResult.Success:
return Request.CreateResponse(successStatusCode, result.Data);
default:
return Request.CreateResponse(HttpStatusCode.NotImplemented);
}
}
#endregion Result handling
}
Now my api controllers almost did not contain the code! Look at the example with really heavy controller:
[RoutePrefix("api/ShoppingList/{shoppingListId:int}/ShoppingListEntry")]
public class ShoppingListEntryController : RikropApiControllerBase
{
private readonly IShoppingListService _shoppingListService;
public ShoppingListEntryController(IShoppingListService shoppingListService)
{
_shoppingListService = shoppingListService;
}
[Route("")]
[HttpPost]
public HttpResponseMessage AddNewEntry(int shoppingListId, SaveShoppingListEntryInput model)
{
model.ShoppingListId = shoppingListId;
var result = _shoppingListService.SaveShoppingListEntry(model);
return Response(result);
}
[Route("")]
[HttpDelete]
public HttpResponseMessage ClearShoppingList(int shoppingListId)
{
var model = new ClearShoppingListEntriesInput {ShoppingListId = shoppingListId, InitiatorId = this.GetCurrentUserId()};
var result = _shoppingListService.ClearShoppingListEntries(model);
return Response(result);
}
[Route("{shoppingListEntryId:int}")]
public HttpResponseMessage Put(int shoppingListId, int shoppingListEntryId, SaveShoppingListEntryInput model)
{
model.ShoppingListId = shoppingListId;
model.ShoppingListEntryId = shoppingListEntryId;
var result = _shoppingListService.SaveShoppingListEntry(model);
return Response(result);
}
[Route("{shoppingListEntry:int}")]
public HttpResponseMessage Delete(int shoppingListId, int shoppingListEntry)
{
var model = new DeleteShoppingListEntryInput
{
ShoppingListId = shoppingListId,
ShoppingListEntryId = shoppingListEntry,
InitiatorId = this.GetCurrentUserId()
};
var result = _shoppingListService.DeleteShoppingListEntry(model);
return Response(result);
}
}
I added an extension method to get current user credentials GetCurrentUserId. If method parameters contains a class that implements IAuthorizedInput that contains 1 property with USerId then I added this info in a global filter. In other cases I need to add this manually. GetCurrentUserId is depend on your authorization method.
It's just a code style, but I called all input models for my business logic with Input suffix (see examples above: DeleteShoppingListEntryInput, ClearShoppingListEntriesInput, SaveShoppingListEntryInput) and result models with output syntax (it's interesting that you no need to declare this types in controller because it's a part of generic class OutputDataModel<TData>).
I'm also used AutoMapper to map my entities to Ouput-classes instead of tons of CreateFromEntity methods.
I'm using an abstraction for data source. In my scenario it was
Repository but this solution has no English documentation then
the better way is to use one of more common solutions.
I also had a base class for my business logic that helps me to create output-models:
public class ServiceBase
{
#region Output parameters
public IOutputDataModel<TData> SuccessOutput<TData>(TData data)
{
return OutputDataModel<TData>.CreateSuccessResult(data);
}
public IOutputDataModel<TData> Output<TData>(OperationResult result, TData data)
{
return OutputDataModel<TData>.CreateResult(result, data);
}
public IOutputDataModel<TData> Output<TData>(OperationResult result)
{
return OutputDataModel<TData>.CreateResult(result);
}
public IOutputModel SuccessOutput()
{
return OutputModel.CreateSuccessResult();
}
public IOutputModel Output(OperationResult result)
{
return OutputModel.CreateResult(result);
}
#endregion Output parameters
}
Finally my "services" with business logic looks like similar to each other. Lets look an example:
public class ShoppingListService : ServiceBase, IShoppingListService
{
private readonly IRepository<ShoppingList, int> _shoppingListRepository;
private readonly IRepository<ShoppingListEntry, int> _shoppingListEntryRepository;
public ShoppingListService(IRepository<ShoppingList, int> shoppingListRepository,
IRepository<ShoppingListEntry, int> shoppingListEntryRepository)
{
_shoppingListRepository = shoppingListRepository;
_shoppingListEntryRepository = shoppingListEntryRepository;
}
public IOutputDataModel<ListModel<ShoppingListDto>> GetUserShoppingLists(GetUserShoppingListsInput model)
{
var shoppingLists =
_shoppingListRepository.Get(q => q.Filter(sl => sl.OwnerId == model.InitiatorId).Include(sl => sl.Entries));
return SuccessOutput(new ListModel<ShoppingListDto>(Mapper.Map<IEnumerable<ShoppingList>, ShoppingListDto[]>(shoppingLists)));
}
public IOutputDataModel<GetShoppingListOutputData> GetShoppingList(GetShoppingListInput model)
{
var shoppingList =
_shoppingListRepository
.Get(q => q.Filter(sl => sl.Id == model.ShoppingListId).Include(sl => sl.Entries).Take(1))
.SingleOrDefault();
if (shoppingList == null)
return Output<GetShoppingListOutputData>(OperationResult.NotFound);
if (shoppingList.OwnerId != model.InitiatorId)
return Output<GetShoppingListOutputData>(OperationResult.AccessDenied);
return
SuccessOutput(new GetShoppingListOutputData(Mapper.Map<ShoppingListDto>(shoppingList),
Mapper.Map<IEnumerable<ShoppingListEntry>, List<ShoppingListEntryDto>>(shoppingList.Entries)));
}
public IOutputModel DeleteShoppingList(DeleteShoppingListInput model)
{
var shoppingList = _shoppingListRepository.Get(model.ShoppingListId);
if (shoppingList == null)
return Output(OperationResult.NotFound);
if (shoppingList.OwnerId != model.InitiatorId)
return Output(OperationResult.AccessDenied);
_shoppingListRepository.Delete(shoppingList);
return SuccessOutput();
}
public IOutputModel DeleteShoppingListEntry(DeleteShoppingListEntryInput model)
{
var entry =
_shoppingListEntryRepository.Get(
q => q.Filter(e => e.Id == model.ShoppingListEntryId).Include(e => e.ShoppingList).Take(1))
.SingleOrDefault();
if (entry == null)
return Output(OperationResult.NotFound);
if (entry.ShoppingList.OwnerId != model.InitiatorId)
return Output(OperationResult.AccessDenied);
if (entry.ShoppingListId != model.ShoppingListId)
return Output(OperationResult.BadRequest);
_shoppingListEntryRepository.Delete(entry);
return SuccessOutput();
}
public IOutputModel ClearShoppingListEntries(ClearShoppingListEntriesInput model)
{
var shoppingList =
_shoppingListRepository.Get(
q => q.Filter(sl => sl.Id == model.ShoppingListId).Include(sl => sl.Entries).Take(1))
.SingleOrDefault();
if (shoppingList == null)
return Output(OperationResult.NotFound);
if (shoppingList.OwnerId != model.InitiatorId)
return Output(OperationResult.AccessDenied);
if (shoppingList.Entries != null)
_shoppingListEntryRepository.Delete(shoppingList.Entries.ToList());
return SuccessOutput();
}
private IOutputDataModel<int> CreateShoppingList(SaveShoppingListInput model)
{
var shoppingList = new ShoppingList
{
OwnerId = model.InitiatorId,
Title = model.ShoppingListTitle,
Entries = model.Entries.Select(Mapper.Map<ShoppingListEntry>).ForEach(sle => sle.Id = 0).ToList()
};
shoppingList = _shoppingListRepository.Save(shoppingList);
return Output(OperationResult.Created, shoppingList.Id);
}
}
Now all routine of creating DTOs, responses and other nonBusinessLogic actions are in the base classes and we can add features in a easiest and clear way. For new Entity creates new "service" (repository will be created automatically in a generic manner) and inherit it from service base. For new action add a method to existing "service" and actions in API. That is all.
It's just a recommendation that not related with question but it is very useful for me to check routings with auto-generated help page. I also used simple client to execute web api queries from the help page.
My results:
Platform-independent & testable business logic layer;
Map business logic result to HttpResponseMessage in base class in a generic manner;
Half-automated security with ActionFilterAttribute;
"Empty" controllers;
Readable code (code conventions and model hierarchy);
I'd suggest to go with the Repository Pattern. Here you have - IMO - an excellent article about it. This should be one of the easiest refactoring you can do.
Following the guidelines from the indicated article you could refactor the code like the following:
Create the base repository interface
public interface IRepository<TEntity, in TKey> where TEntity : class
{
TEntity Get(TKey id);
void Save(TEntity entity);
void Delete(TEntity entity);
}
Create the specialized repository interface:
public interface IDroneDTORepository : IRepository<DroneDTO, int>
{
IEnumerable<DroneDTO> FindAll();
IEnumerable<DroneDTO> Find(int id);
}
Implement the specialized repository interface:
public class DroneDTORepository : IDroneDTORepository
{
private readonly DbContext _dbContext;
public DroneDTORepository(DbContext dbContext)
{
_dbContext = dbContext;
}
public DroneDTO Get(int id)
{
return _dbContext.DroneDTOs.FirstOrDefault(x => x.Id == id);
}
public void Save(DroneDTO entity)
{
_dbContext.DroneDTOs.Attach(entity);
}
public void Delete(DroneDTO entity)
{
_dbContext.DroneDTOs.Remove(entity);
}
public IEnumerable<DroneDTO> FindAll()
{
return _dbContext.DroneDTOs
.Select(d => new DroneDTO
{
iddrones = d.iddrones,
//more stuff
})
.ToList();
}
public IEnumerable<DroneDTO> Find(int id)
{
return FindAll().Where(x => x.iddrones == id).ToList();
}
}
Use the repository in the code:
private IDroneDTORepository _repository = new DroneDTORepository(dbContext);
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = _repository.FindAll();
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = _repository.Find(id);
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
This should be close to the resulting code (obviously something might need changes). Let me know if anything is unclear.
Reusing the Select part (projection) in such scenarios is quite easy.
Let take a look at Queryable.Select method signature
public static IQueryable<TResult> Select<TSource, TResult>(
this IQueryable<TSource> source,
Expression<Func<TSource, TResult>> selector
)
What you call "selection code" is actually the selector parameter. Assuming your entity class is called Drone, then according to the above definition we can extract that part as Expression<Func<Drone, DroneDto>> and reuse it in both places like this
public class APIController : ApiController
{
static Expression<Func<Drone, DroneDto>> ToDto()
{
// The code that was inside Select(...)
return d => new DroneDTO
{
iddrones = d.iddrones,
//more stuff
};
}
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = db.drones.Select(ToDto());
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = db.drones.Where(d => d.iddrones == id).Select(ToDto());
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
}
Of course these two methods can further be refactored (to become "one liners"), but the above is the minimal refactoring that allows reusing the Select part w/o changing any semantics, executing context or the way you write your queries.
Put your mapping to DTO code into a single method that you reuse then you can just do something like:
var drone = db.drones.Select(d => DroneDto.FromDb(d))
.Where(drones => drones.iddrones == id);
public class DroneDto
{
public int iddrones {get;set;}
// ...other props
public static DroneDto FromDb(DroneEntity dbEntity)
{
return new DroneDto
{
iddrones = dbEntity.iddrones,
//... other props
}
}
}
First, try avoid use db directly in the webapi, move to a service.
And second, if I've understand your question, you want avoid write the conversion. You can use AutoMapper, install via nuget with extensions AutoMapper.QueryableExtensions, and configure the mapping between Drone and DroneDto. Configure the mapper:
Mapper.CreateMap<Drone, Dtos.DroneDTO>();
And use as simple as:
db.Drones
.Where(d => ... condition ...)
.Project()
.To<DroneDto>()
.ToList();
Like ben did, you can put your conversion code into a static method on the DroneDto class as such:
public class DroneDto
{
public int iddrones {get;set;}
public static DroneDto CreateFromEntity(DroneEntity dbEntity)
{
return new DroneDto
{
iddrones = dbEntity.iddrones,
...
};
}
}
However, the problem with Bens approach was that the .Select method was called on the DbSet, and LINQ to Entities do not handle these methods. So, you need to do your queries on the DbSet first, then collect the result. For example by calling .ToList(). Then you can do the conversion.
public class APIController : ApiController
{
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = db.drones.ToList().Select(d => DroneDto.CreateFromEntity(d));
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = db.drones.Where(d => d.iddrone == id)
.ToList().Select(d => DroneDto.CreateFromEntity(d));
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
}
Alternatively, if you want to avoid multiple enumerations of the result, have a look at AutoMapper. Specifically the Queryable-Extensions.
The DB Call should be in a separate layer to the web api (reason: separation of concerns: you may want to change the DB technology in the future, and your web API may want to get data from other sources)
Use a factory to build your DroneDTO. If you are using dependency injection, you can inject it into the web api controller. If this factory is simple (is not depended on by other factories) you can get away with making it static, but be careful with this: you don't want to have lots of static factories that depend on each other because as soon as one needs to not be static any more you will have to change all of them.
public class APIController : ApiController
{
private readonly IDroneService _droneService;
public APIController(IDroneService droneService)
{
_droneService = droneService;
}
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage GetDrones()
{
var drones = _droneService
.GetDrones()
.Select(DroneDTOFactory.Build);
return Request.CreateResponse(HttpStatusCode.OK, drones);
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage GetDrones(int id)
{
// I am assuming you meant to get a single drone here
var drone = DroneDTOFactory.Build(_droneService.GetDrone(id));
return Request.CreateResponse(HttpStatusCode.OK, drone);
}
}
public static class DroneDTOFactory
{
public static DroneDTO Build(Drone d)
{
if (d == null)
return null;
return new DroneDTO
{
iddrones = d.iddrones,
//more stuff
};
}
}
Use a separate data access layer. I assumed the GetDrone(int Id) will retrieve one or no drone and used SingleOrDefault(). You can adjust that as needed.
//move all the db access stuff here
public class Db
{
//assuming single drone is returned
public Drone GetDrone(int id)
{
//do SingleOrDefault or Where depending on the needs
Drone drone = GetDrones().SingleOrDefault(drones => drones.iddrones == id);
return drone;
}
public IQueryable<Drone> GetDrones()
{
var drone = db.drones.Select(d => new DroneDTO
{
iddrones = d.iddrones,
//more stuff
});
return drone;
}
}
Then from the client:
public class APIController : ApiController
{
//this can be injected, service located, etc. simple instance in this eg.
private Db dataAccess = new Db();
[HttpGet]
[Route("api/drones")]
public HttpResponseMessage getDrones()
{
var drones = dataAccess.GetDrones();
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drones);
return res;
}
[HttpGet]
[Route("api/drones/{id}")]
public HttpResponseMessage getDrones(int id)
{
var drone = dataAccess.GetDrone(int id);
HttpResponseMessage res = Request.CreateResponse(HttpStatusCode.OK, drone);
return res;
}
}

ASP.NET MVC - HybridViewResult (ViewResult /PartialViewResult)

Is it possible to build a hybrid ViewResult that returns in depedency of an AjaxRequest or HttpRequest a PartialViewResult or ViewResult?
IsAjaxRequest --> return PartialViewResult
!IsAjaxRequest --> return ViewResult
As far as I know my HybridViewResult should derive from ViewResultBase.
But how to implement the FindView method?
Try:
public class HybridViewResult : ActionResult
{
public string ViewName { get; set; }
public HybridViewResult () { }
public HybridViewResult (string viewName ) { this.ViewName = viewName ; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null) throw new ArgumentNullException("context");
var usePartial = ShouldUsePartial();
ActionResult res = GetInnerViewResult(usePartial);
res.ExecuteResult(context);
}
private ActionResult GetInnerViewResult(bool usePartial)
{
var view = ViewName;
ActionResult res;
if(String.IsNullOrEmpty(view)) {
res = usePartial ? new PartialViewResult(view) : new ViewResult(view);
}
else {
res = usePartial ? new PartialViewResult() : new ViewResult();
}
return res;
}
private bool ShouldUsePartial(ControllerContext context) {
return false; //your code that checks if you need to use partial here
}
}
Add any constructor & GetInnerViewResult variations as needed i.e. to pass Model.
This is a slightly more stripped down take on eglasius's answer. I'm actually tackling a similar problem except I need to return a JsonResult.
The (untested) NormalOrAjaxResult simply lets you specify an action result for the non ajax request and one for the ajax request. Because these are ActionResults you can mix up Redirect, View, Partial and Json view results.
public class NormalOrAjaxResult : ActionResult
{
private readonly ActionResult _nonAjaxActionResult;
private readonly ActionResult _ajaxActionResult;
public NormalOrAjaxResult(ActionResult nonAjaxActionResult, ActionResult ajaxActionResult)
{
_nonAjaxActionResult = nonAjaxActionResult;
_ajaxActionResult = ajaxActionResult;
}
public override void ExecuteResult(ControllerContext context)
{
var isAjaxRequest = context.HttpContext.Request["isAjax"];
if (isAjaxRequest != null && isAjaxRequest.ToLower() == "true")
{
_ajaxActionResult.ExecuteResult(context);
} else
{
_nonAjaxActionResult.ExecuteResult(context);
}
}
}
can you not just make 2 different actions in that case? the 'shared' logic you could simply put in a [nonAction] method?
I know I'm really late to the party here, but these didnt seem quite right to me, so here's my 2 cents:
public class PartialViewConverter : ViewResult
{
public ViewResultBase Res { get; set; }
public PartialViewConverter(ViewResultBase res) { Res = res; }
public override void ExecuteResult(ControllerContext context)
{
Res.ExecuteResult(context);
}
public static ViewResult Convert(ViewResultBase res)
{
return new PartialViewConverter(res);
}
}
With usage:
return PartialViewConverter.Convert(PartialView());
And then in your controller if you override View
protected override ViewResult View(string viewName, string masterName, object model)
{
//Whichever condition you like can go here
if (Request.QueryString["partial"] != null)
return PartialViewConverter.Convert(PartialView(viewName, model));
else
return base.View(viewName, masterName, model);
}
Any action method where you return a view will automatically also return partials when requested:
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
//This will return a partial if partial=true is passed in the querystring.
return View();
}

Categories