JsonResult does not contain a constructor that takes 1 argument - c#

I'm currently working with JsonResult. There's a problem: if I call JsonResult in my UserService, I won't be able to call it with a parameter, but if I call it like the same in the UserController, it works with my parameter. So my question now is, the architecture is the same as in the controller so what's wrong?
UserService.cs:
public class UserService : IUserService
{
private readonly IMapper mapper;
private readonly ILogger<UserService> logger;
public UserService(
IMapper mapper,
ILogger<UserService> logger)
{
this.mapper = mapper;
this.logger = logger;
}
private static IList<Contact> GetStaticContacts(string fileName)
{
var jsonText = System.IO.File.ReadAllText(fileName);
var contacts = JsonSerializer.Deserialize<IList<Contact>>(jsonText);
return JsonResult(contacts);
}
Task<IList<Contact>> IUserService.GetNationalCoordinators()
{
return new JsonResult(GetStaticContacts("Test1.json"));
}
Task<IList<Contact>> IUserService.GetLocalCoordinators()
{
return new JsonResult(GetStaticContacts("Test2.json"));
}
Task<IList<Contact>> IUserService.GetMedicalAdvisors()
{
return new JsonResult(GetStaticContacts("Test3.json"));
}
}
UsersController:
public async Task<IActionResult> GetLocalCoordinators(CancellationToken cancellationToken = default)
{
var contacts = await userService.GetLocalCoordinators();
var result = mapper.Map<IList<ContactDto>>(contacts);
return new JsonResult(result);
}

Check this out -> JsonResult in services layer
Also the documentation about JsonResult here https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.jsonresult?view=aspnetcore-6.0 specifies that the JsonResult object comes from ActionResult which is used in the Controller.

Related

Moving Controller's logic to Service layer

As an exercise I was asked to moved logic from controller to service layer. With methods such as Get or GetById it wasn't a problem. Difficult part is to move Update and Patch methods. Below, you will find what I do have in Controler class and how I've changed them to the new requirement.
ToursController.cs before change
public class ToursController : ControllerBase
{
private readonly ToursRepository _repo;
private readonly IMapper _mapper;
public ToursController(ToursRepository repository, IMapper mapper)
{
_repo = repository ?? throw new ArgumentNullException(nameof(repository));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
}
[HttpPut("{id}")]
public async Task<ActionResult> Update(int id, TourForUpdateDto updateDto)
{
var tour = await _repo.GetByIdAsync(id);
if (tour is null)
{
return NotFound();
}
_mapper.Map(updateDto, tour);
await _repo.SaveChangesAsync();
return NoContent();
}
[HttpPatch("{id}")]
public async Task<ActionResult> Patch(int id, JsonPatchDocument patchDocument)
{
var tour = await _repo.GetByIdAsync(id);
if (tour is null)
{
return NotFound();
}
var updateDto = _mapper.Map<TourForUpdateDto>(tour);
patchDocument.ApplyTo(updateDto);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (!TryValidateModel(patchDocument))
{
return BadRequest(ModelState);
}
_mapper.Map(updateDto, tour);
await _repo.SaveChangesAsync();
return NoContent();
}
}
ToursController.cs after change
public class ToursController : ControllerBase
{
private readonly ToursRepository _repo;
private readonly IMapper _mapper;
private readonly ToursService _toursService;
public ToursController(ToursRepository repository, IMapper mapper)
{
_repo = repository ?? throw new ArgumentNullException(nameof(repository));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
_toursService = toursService ?? throw new ArgumentNullException(nameof(toursService));
}
[HttpPut("{id}")]
public async Task<ActionResult> Update(int id, TourForUpdateDto updateDto)
{
if (!_toursService.Exists(id))
{
return NotFound();
}
await _toursService.Update(id, updateDto);
return NoContent();
}
[HttpPatch("{id}")]
public async Task<ActionResult> Patch(int id, JsonPatchDocument patchDocument)
{
// No idea how to update this method.
var tour = await _repo.GetByIdAsync(id);
if (tour is null)
{
return NotFound();
}
var updateDto = _mapper.Map<TourForUpdateDto>(tour);
patchDocument.ApplyTo(updateDto);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (!TryValidateModel(patchDocument))
{
return BadRequest(ModelState);
}
_mapper.Map(updateDto, tour);
await _repo.SaveChangesAsync();
return NoContent();
}
}
ToursService.cs after change
using AutoMapper;
using BandManager.Api.Models;
using BandManager.Api.Services.Repositories;
public class ToursService
{
private readonly ToursRepository _repo;
private readonly IMapper _mapper;
public ToursService(ToursRepository repo, IMapper mapper)
{
_repo = repo ?? throw new ArgumentNullException(nameof(repo));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
}
public async Task Update(int id, TourForUpdateDto updateDto)
{
var tour = await GetByIdAsync(id);
if (tour is null)
return;
_mapper.Map(updateDto, tour);
await _repo.SaveChangesAsync();
}
public async Task<TourDto?> GetByIdAsync(int id)
{
return _mapper.Map<TourDto>(await _repo.GetByIdAsync(id));
}
public bool Exists(int id)
{
return _repo.Exists(id);
}
}
Questions and thoughts
Should I ever return objects which are implementing IActionResult or any MVC related types from my services? My gut feeling is - no.
Should service be able to use AutoMapper to convert entities to DTOs? I think, yes.
Should my service, in the same method call, be able to call SaveChangesAsync on repository?

C# API query for Alternate key

I'm trying to do this query: SELECT * FROM Machine WHERE KlantId = [Id from url] in c# but its returing only 1 row when it should be 2 rows. I manualy change the input of KlantId in the url. The url looks like this: https://00.00.00.00:0000/api/Machine/1
Any help would be much appreciated.
Api output:
Api Model:
[
{
"SerienummerId": 1,
"Serienummer": "10K13321",
"Bouwjaar": "2020",
"Urenstand": "10",
"Locatie": "-23134123, 123123",
"KlantId": "1"
},
{
"SerienummerId": 2,
"Serienummer": "25K84314",
"Bouwjaar": "1998",
"Urenstand": "5010",
"Locatie": "-23134123, 123123",
"KlantId": "1"
}
]
C# MachineRepository
public class MachineRepository: IRepository<MachineModel>
{
private readonly DataContext _context;
public MachineRepository(DataContext context)
{
_context = context;
}
// Get Service by id
public async Task<MachineModel> GetDataById(int id)
{
return await _context.Machine.FirstOrDefaultAsync(x => x.KlantId == id);
}
}
MachineController:
[ApiController]
[Route("api/Machine")]
[Produces("application/json")]
public class MachineController : ControllerBase
{
private readonly IRepository<MachineModel> _repo;
private readonly IMapper _mapper;
public MachineController(IRepository<MachineModel> repo, IMapper mapper)
{
_repo = repo;
_mapper = mapper;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetServiceById(int id)
{
Console.WriteLine("This is the comming id ");
Console.WriteLine(id);
var model = await _repo.GetDataById(id);
return Ok(_mapper.Map<MachineDto>(model));
}
}
FirstOrDefaultAsync will return the top 1 record.
Change 1 - In C# MachineRepository, Instead of FirstOrDefaultAsync, replace this with Where and change the return type to List
Change 2 - In MachineController, Map it to List DTO
Note: I assume Machine is having array of records
public class MachineRepository: IRepository<MachineModel>
{
private readonly DataContext _context;
public MachineRepository(DataContext context)
{
_context = context;
}
// Get Service by id
public async Task<List<MachineModel>> GetDataById(int id) //change the return type to LIST
{
return await _context.Machine.Where(x => x.KlantId == id).ToListAsync();
}
}
[ApiController]
[Route("api/Machine")]
[Produces("application/json")]
public class MachineController : ControllerBase
{
private readonly IRepository<MachineModel> _repo;
private readonly IMapper _mapper;
public MachineController(IRepository<MachineModel> repo, IMapper mapper)
{
_repo = repo;
_mapper = mapper;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetServiceById(int id)
{
Console.WriteLine("This is the comming id ");
Console.WriteLine(id);
var model = await _repo.GetDataById(id);
return Ok(_mapper.Map<List<MachineDto>>(model)); //Map to List
}
}

DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type

It was working earlier before adding actionedService which is similar to rejectionService, throws following error
An unhandled exception occurred while processing the request.
DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Sx.Workflow.Server.Controllers.ReportController' can be invoked with the available services and parameters:
Cannot resolve parameter 'Sx.Workflow.Reporting.Services.IActionedService actionedService' of constructor 'Void .ctor(NLog.ILogger, AutoMapper.IMapper, Sx.Workflow.Reporting.Services.IRejectionService, Sx.Workflow.Reporting.Services.IActionedService)'.
Autofac.Core.Activators.Reflection.ReflectionActivator.GetValidConstructorBindings(ConstructorInfo[] availableConstructors, IComponentContext context, IEnumerable parameters) in ReflectionActivator.cs, line 160
Controller
namespace Sx.Workflow.Server.Controllers
{
[MenuItem("report")]
[ServiceFilter(typeof(SettingsFilter))]
[Authorize(Policy = Security.Constants.RolePolicy)]
public class ReportController : Controller
{
private readonly ILogger _logger;
private readonly IMapper _mapper;
private readonly IRejectionService _rejectionService;
private readonly IActionedService _actionedService;
public ReportController(ILogger logger, IMapper mapper, IRejectionService rejectionService, IActionedService actionedService)
{
_logger = logger;
_mapper = mapper;
_rejectionService = rejectionService;
_actionedService = actionedService;
}
[HttpGet]
public IActionResult Index()
{
_logger.Info("Report Controller");
return View();
}
[HttpPost]
[ApiExceptionFilter]
public async Task<IActionResult> Reject(RejectionReportRequestDto criteria)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_logger.Info("Generate Rejection Report");
var result = await _rejectionService.Generate(criteria.From, criteria.To);
var items = _mapper.Map<RejectionReportDto>(result);
return Ok(items);
}
[HttpPost]
[ApiExceptionFilter]
public async Task<IActionResult> Actioned(ActionedReportRequestDto criteria)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_logger.Info("Generate Actioned Report");
var result = await _actionedService.Generate(criteria.From, criteria.To);
var items = _mapper.Map<ActionedReportDto>(result);
return Ok(items);
}
}
}
Handler
namespace Sx.Workflow.Reporting.Handlers
{
public class ActionedReportHandler : IHandleEvent<ApplicationActionedEvent>
{
private readonly IActionedService _service;
public ActionedReportHandler(IActionedService service)
{
_service = service;
}
public Task Handle(ApplicationActionedEvent args)
{
var actioned = new Actioned
{
ApplicationNumber = args.ApplicationNumber,
AssigneeFrom = args.AssigneeFrom,
AssigneeTo = args.AssigneeTo,
DepartmentFrom = args.DepartmentFrom.Name,
DepartmentTo = args.DepartmentTo.Name,
Reason = args.RejectReasonName,
Comments = args.RejectReasonText,
RejectionDate = DateTime.Now
};
return _service.Save(actioned);
}
}
}
Service
namespace Sx.Workflow.Reporting.Services
{
public class ActionedService : IActionedService
{
private readonly ISaveActioned _saveActioned;
private readonly IGenerateActionedReport _actionedReport;
public ActionedService(ISaveActioned saveActioned, IGenerateActionedReport actionedReport)
{
_saveActioned = saveActioned;
_actionedReport = actionedReport;
}
public Task<ActionedReport> Generate(DateTime from, DateTime to)
{
return _actionedReport.Generate(from, to);
}
public Task Save(Actioned actioned)
{
return _saveActioned.Save(actioned);
}
}
}
Interface
namespace Sx.Workflow.Reporting.Services
{
public interface IActionedService
{
Task Save(Actioned actioned);
Task<ActionedReport> Generate(DateTime from, DateTime to);
}
}
Service Module
public class ServiceModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<RejectionService>()
.As<IRejectionService>()
.InstancePerLifetimeScope();
builder.RegisterType<ActionedService>()
.As<IActionedService>()
.InstancePerLifetimeScope();
}
}
Makes sense. While you are registering the type in DI, you have nothing for:
public ActionedService(ISaveActioned saveActioned, IGenerateActionedReport actionedReport)
So autofac assumes that there must be an empty constructor in ActionedService
So there are 2 solutions:
Remove the constructor parameters and create them without DI
Create the registrations for the two parameters of the constructor. Something like the following:
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<RejectionService>()
.As<IRejectionService>()
.InstancePerLifetimeScope();
builder.RegisterType<ActionedService>()
.As<IActionedService>()
.InstancePerLifetimeScope();
builder.RegisterType<SaveActioned>()
.As<ISaveActioned>()
.InstancePerLifetimeScope();
builder.RegisterType<GenerateActionedReport>()
.As<IGenerateActionedReport>()
.InstancePerLifetimeScope();
}

How can I mock instance in ASP.NET Core unit test using Moq?

Writing unit test with nunit in asp.net core service, I try to write controller unit test.
[ApiController]
public class ConvertController : ControllerBase
{
private readonly ILogger<ConvertController> _logger;
private readonly Factory.IWorkbookFactory _workbookFactory;
public ConvertController(ILogger<ConvertController> logger, Factory.IWorkbookFactory workbookFactory)
{
_logger = logger;
_workbookFactory = workbookFactory;
}
[HttpPost]
[Route("api/v1/[controller]/pdf")]
public ConvertResponse Post(ConvertRequest req)
{
ConvertResponse res = new ConvertResponse();
res.OutputData = new Converter(_workbookFactory.newInstance()).ExcelToPDF(req.InputData);
return res;
}
}
In this case, can I mock whole Converter instance?
#naruke, in order to mock `Converter' type it need to be injected so the code shall have to be modified as below.
Firstly, implement the Converterlike this.
Create interface IConverter.
public interface IConverter
{
/*OutPut */ ExcelToPDF(/* input*/);
}
then implement
class Converter:IConverter
{
....
}
Now no need to inject IWorkbookFactory
[ApiController]
public class ConvertController : ControllerBase
{
private readonly ILogger<ConvertController> _logger;
//private readonly Factory.IWorkbookFactory _workbookFactory;
private readonly IConverter _converter;
public ConvertController(ILogger<ConvertController> logger,IConverter converter // Factory.IWorkbookFactory workbookFactory)
{
_logger = logger;
//_workbookFactory = workbookFactory;
_converter=converter;
}
[HttpPost]
[Route("api/v1/[controller]/pdf")]
public ConvertResponse Post(ConvertRequest req)
{
ConvertResponse res = new ConvertResponse();
res.OutputData = _converter.ExcelToPDF(req.InputData);
return res;
}
}
hope this helps.

I want to Create Xunit test for this controller. How can i do that

I have created small kind of xunit test case but I don't know how to create this controller which i have mention below.
public class PropertyController : ControllerBase
{
private readonly IMediator _mediator;
private readonly ILogger<PropertyController> _logger;
public PropertyController(IMediator mediator, ILogger<PropertyController> logger)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<IActionResult> AddProperty([FromBody] AddPropertyCommand command)
{
bool commandResult = false;
_logger.LogInformation(
"----- Sending command: {CommandName} - {IdProperty}: {CommandId} ({#Command})",
command.GetGenericTypeName(),
nameof(command.ModifiedUserId),
command.ModifiedUserId,
command);
commandResult = await _mediator.Send(command);
if (!commandResult)
{
return BadRequest();
}
return Ok();
}
I have created like this. i have mock the dependency and create a test case for add command is working fine or not
public class PropertyControllerTest
{
private readonly PropertyController _it;
private readonly Mock<IMediator> _mediatorMock;
private readonly Mock<ILogger<PropertyController>> _loggerPropertycontrollerMock;
public PropertyControllerTest()
{
_mediatorMock = new Mock<IMediator>();
_loggerPropertycontrollerMock = new Mock<ILogger<PropertyController>>();
_it = new PropertyController(_mediatorMock.Object, _loggerPropertycontrollerMock.Object);
}
[Fact]
public void it_Should_add_information_successfully_and_returns_200_status_result()
{
//How can i write xunit test case. I'm creating like this
_mediatorMock.Setup(x => x.Send().Returns(property);
}
The test below covers the 200 status result - a similar test for bad requests would be very similar.
[Fact]
public void it_Should_add_information_successfully_and_returns_200_status_result()
{
// Arrange
var expected = new AddPropertyCommand();
_mediatorMock.Setup(x => x.Send(It.IsAny<AddPropertyCommand>())).Returns(true);
// Act
var actionResult = _it.AddProperty(expected);
// Assert
actionResult.ShouldBeAssignableTo<OkResult>();
_mediatorMock.Verify(x => x.Send(expected));
}
N.B. actionResult.ShouldBeAssignableTo<OkResult>(); is written using the Shouldly assertion framework, you can swap that out for anything you like. The one built into XUnit would be like this: Assert.IsType(typeof(OkResult), actionResult);

Categories

Resources