Controller Query Parameter (not in action) - c#

I'm trying to make a api like the OPENBANKPROJECT. Such as
/api/banks/{BANK_ID}/atms/{ATM_ID} etc. I guess banks and atm is a different controller
I want get a global (api/Claim/{id}/Detail/[action] i need this {id}) parameter before before [action] initialize (maybe in constructor).
How do i get this {id} before [action] initialize?
[Route("api/Claim/{id}/Detail/[action]")]
public class ClaimDetailController
{
int _id; // assignment {id}
public ClaimDetailController(IClaimDetailService claimDetailService)
{
`Need Query Id before execute action`
}
[HttpPost]
public async Task<BaseResponse> ClaimDetailInfoPolicy(ClaimDetailKeyModel model)
{
return `codes with _id`;
}
}
public class ClaimDetailKeyModel
{
public long FileNo { get; set; }
public long RecourseNo { get; set; }
}

Solution was simple :)
[ApiController]
[Route("api/Claim/{claimId}/Detail/[action]/")]
public class ClaimDetailController
{
[FromRoute(Name = "claimId")]
public int Id { get; set; }
public ClaimDetailController(IClaimDetailService claimDetailService)
{
`bla bla`
}
[HttpPost]
public async Task<BaseResponse> ClaimDetailInfoPolicy(ClaimDetailKeyModel model)
{
return `codes with Id`
}
}

Add this id to your request handler.
[HttpPost]
public async Task<BaseResponse> ClaimDetailInfoPolicy(int id, ClaimDetailKeyModel model)
{
return `codes`;
}

Related

Bind new modal field in asp.net core modal

My code was working file until i added a new field in modal which has only get method
public bool hasShiftingRequest {
//this field is not in database
//it is being calculated everytime you access it
get
{
return _context.AssetShifting.Where(a => a.assetId == this.Id & a.status.Equals("REQUESTED")).Any();
}
}
But it is causing error during my edit method which is binding fronted data with modal
(Basically problem during Binding)
public async Task<IActionResult> Edit(int id, [Bind("Id,make_model,lot,username,email")] AssetDataPc assetDataPc)
and I am getting this error
Please Help !
EDIT
My assetPC modal
public class AssetDataPc
{
public readonly AssetManagementContext _context;
public AssetDataPc(AssetManagementContext context)
{
_context = context;
}
public int ram { get; set; }
[Display(Name = "Remarks")]
public string remarks { get; set; }
[Display(Name = "QR Code Status")]
public string qr_code_status { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
[Display(Name = "Last updated")]
public DateTime updated_at { get; set; } = DateTime.Now;
[EmailAddress]
[Display(Name = "Email")]
public string email { get; set; }
[Display(Name = "Screen Size")]
public string screen_size { get; set; }
[Display(Name = "Color")]
public string rowColor { get; set; } = "";
public bool hasShiftingRequest {
//this field is not in database
//it is being calculated everytime you access it
get
{
return _context.AssetShifting.Where(a => a.assetId == this.Id & a.status.Equals("REQUESTED")).Any();
}
}
}
EDIT 2
my edit (POST( method is some what like this
public async Task<IActionResult> Edit(int id, [Bind("remarks,qr_code_status,email")] AssetDataPc assetDataPc)
{
if (ModelState.IsValid)
{
assetDataPc.updated_at = DateTime.Now;
_context.Update(assetDataPc);
await _context.SaveChangesAsync();
}
EDIT 3
My edit (Get) method:
public async Task<IActionResult> Edit(int? id)
{
var assetDataPc = await _context.AssetDataPcs.FindAsync(id);
if (assetDataPc == null)
{
return NotFound();
}
return View(assetDataPc);
}
hasShiftingRequest is not in your database?
Then use [NotMapped] if you need to use extra column without adding this column in database so that entity framework core will not check this matching column between model class and table in database.
[NotMapped]
public bool? hasShiftingRequest { get; set; }
Remove AssetManagementContext from your AssetDataPc model. Like this.
public class AssetDataPc
{
[NotMapped]
public bool? hasShiftingRequest { get; set; }
}
"Get" Edit method
public async Task<IActionResult> Edit(int? id)
{
var assetDataPc = await _context.AssetDataPcs.FindAsync(id);
if (assetDataPc == null)
{
return NotFound();
}
else
assetDataPc.hasShiftingRequest = _context.AssetShifting.Where(a => a.assetId == assetDataPc.Id & a.status.Equals("REQUESTED")).Any();
return View(assetDataPc);
}
Solution
--dont use DbContext in modal classes
--use [NotMapped] to avoid creating database field
As the exception states AssetDataPc should have a parameterless constructor in order to be binded. When you added this constructor
public AssetDataPc(AssetManagementContext context)
{
_context = context;
}
it started failing.
Consider moving hasShiftingRequest logic outside the class and just map result to plain property.
As the error message said, Model bound complex types must not be abstract or value types and must have a parameterless constructor. So, you could try to add the default AssetDataPc constructor for the AssetDataPc class.
public class AssetDataPc
{
public readonly AssetManagementContext _context;
public AssetDataPc(){} //add default constructor
public AssetDataPc(AssetManagementContext context)
{
_context = context;
}
...
public bool hasShiftingRequest {
//this field is not in database
//it is being calculated everytime you access it
get
{
return _context.AssetShifting.Where(a => a.assetId == this.Id & a.status.Equals("REQUESTED")).Any();
}
}
}

How to write separately business logic in API controller as helper class or handler using interfaces

I'm developing .net RESTful WebAPI with API controller with actions, using entity framework.
It will auto generate the controller, So I need to write a Business logic in controller(EmployeeDetailController) to helper(EmployeeDetailHandler) class and connect with Interface(IEmployeeDetailHandler).
So need to connect Dbcontext to helper class(EmployeeDetailHandler) without depending on controller to test the unit testing with xunit.
How can i Write Handler class and the controller class?
This is my Controller(API controller with actions, using entity framework).
I need to write the logic inside this in separately on helper class by connecting interface to controller class.
[Route("api/[controller]")]
[ApiController]
public class EmployeeDetailController : ControllerBase
{
private readonly AuthenticationContext _context;
public EmployeeDetailController(AuthenticationContext context)
{
_context = context;
}
// GET: api/EmployeeDetail
[HttpGet]
public IEnumerable<EmployeeDetail> GetEmployeeDetails()
{
return _context.EmployeeDetails;
}
// GET: api/EmployeeDetail/5
[HttpGet("{id}")]
public async Task<IActionResult> GetEmployeeDetail([FromRoute] int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var employeeDetail = await _context.EmployeeDetails.FindAsync(id);
if (employeeDetail == null)
{
return NotFound();
}
return Ok(employeeDetail);
}
// PUT: api/EmployeeDetail/5
[HttpPut("{id}")]
public async Task<IActionResult> PutEmployeeDetail([FromRoute] int id, [FromBody] EmployeeDetail employeeDetail)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != employeeDetail.EId)
{
return BadRequest();
}
_context.Entry(employeeDetail).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!EmployeeDetailExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/EmployeeDetail
[HttpPost]
public async Task<IActionResult> PostEmployeeDetail([FromBody] EmployeeDetail employeeDetail)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.EmployeeDetails.Add(employeeDetail);
await _context.SaveChangesAsync();
return CreatedAtAction("GetEmployeeDetail", new { id = employeeDetail.EId }, employeeDetail);
}
// DELETE: api/EmployeeDetail/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteEmployeeDetail([FromRoute] int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var employeeDetail = await _context.EmployeeDetails.FindAsync(id);
if (employeeDetail == null)
{
return NotFound();
}
_context.EmployeeDetails.Remove(employeeDetail);
await _context.SaveChangesAsync();
return Ok(employeeDetail);
}
private bool EmployeeDetailExists(int id)
{
return _context.EmployeeDetails.Any(e => e.EId == id);
}
}
}
This is my Context class
public class AuthenticationContext : IdentityDbContext
{
public AuthenticationContext(DbContextOptions options):base(options {}
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<EmployeeDetail> EmployeeDetails { get; set; }
}
This is dB Model
public class EmployeeDetail
{
[Key]
public int EId { get; set; }
[Required]
[Column(TypeName = "Nvarchar(100)")]
public string EmployeeName { get; set; }
[Required]
[Column(TypeName = "Nvarchar(10)")]
public string PhoneNo { get; set; }
[Required]
[Column(TypeName = "Nvarchar(10)")]
public string BDay { get; set; }
[Required]
[Column(TypeName = "Nvarchar(10)")]
public string Nic { get; set; }
}
Expect is relevant handler class( service/Hepler) with interfaces.
Unit testing is for small bits of code you can test in isolation.
Forget about controllers, forget about DbContext and focus on your business logic.
Your controllers should be thin layers doing model validation and passing data to your business layer, nothing more. As such you should have no need to even look at them.
So, unit test the business logic. Cover every endpoint with integration tests, for correct and incorrect models, etc.
Orchestrate the integration tests with something like Postman or anything else that allows this.
The code you showed is not suitable for unit testing at all.

How to retrieve an object property inside a view

I have an index method in a controller which looks like this :
public ActionResult Index()
{
var object = _ObjectService.GetAll();
return View(object);
}
Which give me a list of object with those properties :
public class Object : EntityWithNameAndId
{
public virtual Site Site { get; set; }
public virtual List<User> Users { get; set; }
public virtual List<Planning> Plannings { get; set; }
public virtual Guid IdPilote { get; set; }
}
Now in my Index() view, i want to get the User who's related to the IdPilote id and display its name.
I tried something like this, thanks to this topic ASP.Net MVC: Calling a method from a view :
#model List<MyClass.Models.Promotion>
#foreach (var item in Model)
{
<td>#item.Site.Name</td>
#{
var id = item.IdPilote;
//Here Interface and Service are folders
var user = MyDAL.Interface.Service.IUserService.Get(id);
}
<td>
//This is where i try to display my User name,
//that i get dynamically using the idPilote for each User in list
</td>
}
But Get(id) is not recognize as a valid method..
public interface IUserService : IDisposable
{
User Get(Guid id);
}
public class UserService : IUserService
{
private MyContext context;
public UserService(MyContext context)
{
this.context = context;
}
public User Get(Guid id)
{
return context.User.Where(w => w.Id == id).SingleOrDefault();
}
}
So what's the best way to get my User object inside my view, since i only get an Id ?
Should i create a new list, using the first one, in my Index method (where i can call IUserInterface.Get()) or is there a better way to do it ?
Make it worked by creating a new list and a specific ViewModel, as suggested :
public class IndexObjectViewModel : EntityWithNameAndId
{
public virtual Site Site { get; set; }
public virtual List<User> Users { get; set; }
public virtual List<Planning> Plannings { get; set; }
//To store User instead of its Id
public virtual User Pilote { get; set; }
}
Now Index() looks like this :
public ActionResult Index()
{
var objects = _IObjectService.GetAll();
ViewBag.NotPromoExist = false;
var indexObj = new List<IndexObjectViewModel>();
foreach (var p in objects)
{
var indexModel = new IndexObjectViewModel();
indexModel.Id = p.Id;
indexModel.Name = p.Name;
indexModel.Site = p.Site;
indexModel.Users = p.Users;
indexModel.Plannings = p.Plannings;
indexModel.Pilote = _IUserService.Get(p.IdPilote);
indexObj.Add(indexModel);
}
return View(indexObj);
}
Everything is done in the controller now. Not sure if it's the best way to do it though..

Exclude controller name from routing in net core asp

I am using asp net core mvc and trying to develop a RESTful app. Let's say I have models like this:
internal class Album
{
public long Id { get; set; }
public string Name { get; set; }
public long ExecutorId { get; set; }
public virtual Executor Executor { get; set; }
}
internal class Executor
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Album> Albums { get; set; }
}
And I have 2 controllers, that I wrote like this:
[ApiVersion("1")]
[Route("api/v{api-version:apiVersion}/[controller]")]
public class ExecutorsController : Controller
{
[HttpGet("{id}", Name = "GetExecutor")]
public IActionResult GetById(long id)
{
//some code
}
[HttpGet(Name = "GetExecutors")]
public IActionResult GetAll()
{
//some code
}
}
[ApiVersion("1")]
[Route("api/v{api-version:apiVersion}/[controller]")]
public class AlbumController : Controller
{
[HttpGet("{id}", Name = "GetAlbum")]
public IActionResult GetById(long id)
{
//some code
}
[HttpGet(Name = "GetAlbums")]
public IActionResult GetAll()
{
//some code
}
}
I can call http://localhost:48234/api/v1/Album/1 and get album by Id,
I can call http://localhost:48234/api/v1/Album and get all albums. The same thing works with executors. So, what I want to do, is to be able to get albums by executorId and that my route would look like
http://localhost:48234/api/v1/executors/1/albums, which will return all albums for executor with Id = 1. The problem is that I would like to put this action into AlbumsController:
[HttpGet]
[Route("executors/{executorId}/albums")]
public IActionResult GetAlbumsByExecutorId(long executorId)
{
return new ObjectResult(_service.GetAlbumsByExecutorId(executorId));
}
This code works just fine, but it puts ControllerName (Albums) at the beginning. How can I remove ControllerName from my route? Or maybe I shouldn't do this and just put this action to ExecutorsController? I decided to do it like this, because if action returns albums, it should be placed in AlbumsController. Am I wrong?
Just remove [controller] from route path in controller and move it to the methods.
Something like this:
[ApiVersion("1")]
[Route("api/v{api-version:apiVersion}/")]
public class AlbumController : Controller
{
[HttpGet("album/{id}", Name = "GetAlbum")]
public IActionResult GetById(long id)
{
//some code
}
[HttpGet("album", Name = "GetAlbums")]
public IActionResult GetAll()
{
//some code
}
[HttpGet]
[Route("executors/{executorId}/albums")]
public IActionResult GetAlbumsByExecutorId(long executorId)
{
return new ObjectResult(_service.GetAlbumsByExecutorId(executorId));
}
}
If you want to get albums, you should place action method in AlbumController.
I'd like to recommend you this way:
[HttpGet(Name = 'GetAlbumsByExecutorId')]
[Route("{executorId}/albums")]
public IActionResult GetAlbumsByExecutorId(long executorId)
{
return new ObjectResult(_service.GetAlbumsByExecutorId(executorId));
}

How to send an object to MVC controller using AngularJS

I have an object, I need to send it to my MVC controller;
object:
params = {Id: 1, UserAge: 32 }
// Angular $htttp
function test() {
var request = $http.post('/user/updateuser/', params);
return request;
}
c# - controller
public class User
{
public int Id { get; set; }
public int UserAge { get; set; }
}
[HttpPost]
public void UpdateUser(User user)
{
//user Id and user age is always zero.
}
UserId and UserAge is always 0 i'm not sure what i'm missing.
Try to add [FromBody] attribute before the input parameter:
public void UpdateUser([FromBody]User user) {}

Categories