Table value parameters not working with Get method - c#

Actually I'm working using dapper.
LoginAuditModel:
public class LoginAuditModel
{
public IList<GuidIdTableType> UserIdTableType { get; set; } = new List<GuidIdTableType>();
public DateTime StartingDate { get; set; }
public DateTime EndingDate { get; set; }
}
Repository:
public async Task<IEnumerable<LoginAuditGetViewModel>> LoginAuditGet(LoginAuditModel model)
{
try
{
async Task<IEnumerable<LoginAuditGetViewModel>> DoLoginAuditGet()
{
using (var connection = _connectionManager.GetOpenConnection(_configuration.GetConnectionString(connectionstring)))
{
return await connection.QueryAsync<LoginAuditGetViewModel>("[dbo].[spName]", param: new
{
UserIdTableType = ((List<GuidIdTableType>)model.UserIdTableType).ToDataTable(),
model.StartingDate,
model.EndingDate
}
, commandType: CommandType.StoredProcedure);
}
}
return await DoLoginAuditGet();
}
catch (Exception ex)
{
throw ex;
}
}
Service:
public async Task<IEnumerable<LoginAuditGetViewModel>> LoginAuditGet(LoginAuditModel model)
{
async Task<IEnumerable<LoginAuditGetViewModel>> DoLoginAuditGet()
{
return await _employeeRepository.LoginAuditGet(model);
}
return await DoLoginAuditGet();
}
Controller:
[HttpGet]
public async Task<IActionResult> LoginAuditGet([FromQuery]LoginAuditModel model)
{
try
{
async Task<IActionResult> DoLoginAuditGet()
{
var rModel = await _employeeService.LoginAuditGet(model);
if (rModel is null || !rModel.Any()) return NotFound();
return Ok(rModel);
}
return await DoLoginAuditGet();
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
When I execute this code using swagger in my case, table valued parameter is always passing with count = 0 (UserIdTableType), but for some reason, when I change controller method to [HttpPost]
it pass parameter correctly! and everything it's working fine:
[HttpPost]
public async Task<IActionResult> LoginAuditGet(LoginAuditModel model)
{
try
{
async Task<IActionResult> DoLoginAuditGet()
{
var rModel = await _employeeService.LoginAuditGet(model);
if (rModel is null || !rModel.Any()) return NotFound();
return Ok(rModel);
}
return await DoLoginAuditGet();
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
So, my question is why it is working as a Post method and not with Get? I need to change something to works with Get? Regards

In your case, you want to send an array of objects as a query string in URL, I think this is impossible, but you can send an array of base data types such as int, string ... etc.
but in Post and Put, it is sending the data as body and have another type of data transfer.
and you should know, there is a limitation on Query String length, you can have a look here: click me

Related

Why Visual Studio automatically creates ApiController with all async methods? How to create ApiController with sync methods in it?

I have created a asp.net core webapi controller named StudentsController.cs using the functionality provided by Visual Studio which is API Controller with actions,using Entity Framework. It implements all the controller methods with return type async Task<ActionResult<>> which is asynchronous method implementation and it is auto generated by Visual Studio
Question is Why it creates all the methods async by its own and how can I create all the methods synchronously using the same auto generate feature of Visual Studio?
async example of controller
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Students.Models;
namespace Students.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class StudentsController : ControllerBase
{
private readonly StudentContext _context;
public StudentsController(StudentContext context)
{
_context = context;
}
// GET: api/Students
[HttpGet]
public async Task<ActionResult<IEnumerable<Student>>> GetStudents()
{
return await _context.Students.Include(d => d.Department).ToListAsync();
}
// GET: api/Students/5
[HttpGet("{id}")]
public async Task<ActionResult<Student>> GetStudent(int id)
{
var student = await _context.Students.Include(d => d.Department).FirstOrDefaultAsync(i => i.SId == id);
if (student == null)
{
return NotFound();
}
return student;
}
// PUT: api/Students/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPut("{id}")]
public async Task<IActionResult> PutStudent(int id, Student student)
{
if (id != student.SId)
{
return BadRequest();
}
_context.Departments.Update(student.Department);
await _context.SaveChangesAsync();
_context.Entry(student).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!StudentExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return Ok();
}
//[HttpPut]
//public async Task<IActionResult> PutStudent(Student student)
//{
// _context.Departments.Update(student.Department);
// await _context.SaveChangesAsync();
// _context.Entry(student).State = EntityState.Modified;
// try
// {
// await _context.SaveChangesAsync();
// }
// catch (DbUpdateConcurrencyException)
// {
// if (!StudentExists(student.SId))
// {
// return NotFound();
// }
// else
// {
// throw;
// }
// }
// return Ok();
//}
// POST: api/Students
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://aka.ms/RazorPagesCRUD.
[HttpPost]
public async Task<ActionResult<Student>> PostStudent(Student student)
{
_context.Students.Add(student);
await _context.SaveChangesAsync();
return CreatedAtAction("GetStudent", new { id = student.SId }, student);
}
[HttpPost]
[Route("StudentList")]
public async Task<ActionResult<Student>> PostStudentList([FromBody] List<Student> student)
{
try
{
foreach (Student s in student)
{
_context.Students.Add(s);
}
_context.SaveChanges();
return CreatedAtAction("GetStudents", student, _context.Students.Include(d => d.Department));
}
catch(Exception ex)
{
return BadRequest();
}
}
// DELETE: api/Students/5
[HttpDelete("{id}")]
public async Task<ActionResult<Student>> DeleteStudent(int id)
{
var student = await _context.Students.FindAsync(id);
if (student == null)
{
return NotFound();
}
_context.Students.Remove(student);
await _context.SaveChangesAsync();
return student;
}
private bool StudentExists(int id)
{
return _context.Students.Any(e => e.SId == id);
}
}
}
sync example of controller
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CourseCRUD.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace CourseCRUD.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class SubjectController : Controller
{
private readonly CourseContext _context;
public SubjectController(CourseContext context)
{
_context = context;
}
[HttpGet]
// GET:api/subject
public IActionResult GetSubjects()
{
try
{
var subjects = _context.subjects.ToList();
return Ok(subjects);
}
catch
{
return BadRequest();
}
}
[HttpPost]
public IActionResult AddSubject([FromBody]Subject subject)
{
try
{
_context.subjects.Add(subject);
_context.SaveChanges();
return CreatedAtAction("GetSubjets", subject);
}
catch
{
return BadRequest();
}
}
[HttpGet("{id}")]
public IActionResult GetSuject(int id)
{
try
{
var subject = _context.subjects.Find(id);
return Ok(subject);
}
catch
{
return BadRequest();
}
}
[HttpPut("id")]
[HttpPut("{id}")]
public IActionResult UpdateSubject(int id, Subject subject)
{
if (id != subject.SubjectId)
{
return BadRequest();
}
_context.Entry(subject).State = EntityState.Modified;
try
{
_context.SaveChanges();
return Ok(subject);
}
catch (DbUpdateConcurrencyException)
{
if (!SubjectDetailExist(id))
{
return NotFound();
}
else
{
throw;
}
}
}
private bool SubjectDetailExist(int id)
{
throw new NotImplementedException();
}
[HttpDelete("{id}")]
public IActionResult DeleteSubject(int id)
{
var result = _context.subjects.Find(id);
if (result == null)
{
return NotFound();
}
_context.subjects.Remove(result);
try
{
_context.SaveChanges();
return Ok(result);
}
catch
{
return BadRequest();
}
}
}
}```
it's a long time since I did anything with t4 templates so a bit rusty but you should perhaps start here
https://learn.microsoft.com/en-us/visualstudio/modeling/code-generation-and-t4-text-templates?view=vs-2019
You specify templates in text files and use rules to generate the required code.
<## output extension=".cs" #>
<## assembly name="System.Xml" #>
<#
System.Xml.XmlDocument configurationData = ...; // Read a data file here.
#>
namespace Fabrikam.<#= configurationData.SelectSingleNode("jobName").Value #>
{
... // More code here.
}
Depending on the values in the XML file, the generated .cs file would resemble the following:
namespace Fabrikam.FirstJob
{
... // More code here.
}
I don't know if controller scaffolding uses t4 templates but it wouldn't suprise me.. or something similar.
The async actions are generated by templates of scaffolding which locates in path similar to C:\ProgramFiles\dotnet\sdk\NuGetFallbackFolder\microsoft.visualstudio.web.codegenerators.mvc\2.0.3\Templates
You could change the template manually.Refer to
https://stackoverflow.com/a/39503291/10158551

How to create configuration for a controller manually so I can use a generic try catch function

I currently have the below code which I thought would work however I am receiving a "HttpControllerContext.Configuration must not be null" error when I create the Ok result. The goal is to be able to call any function in a controller in one line to keep my controllers clean. Such as "return ApiUtilities.TryCatch(() => _someService.Get(id));"
I only have access to 'Ok()', "NotFound()" and "InternalServerError()" because the ApiUtilities Class inherits from ApiController
public IHttpActionResult TryCatch<T>(Func<T> operation)
{
try
{
if (ModelState.IsValid)
{
var result = operation();
return Ok(result);
}
}
else
{
return BadRequest();
}
}
catch (Exception error)
{
return InternalServerError();
}
Edit:
My controller looks like this
public class PageController : ApiController
{
private ISomeService _someService;
private ApiUtilities _apiUtilities;
public PageController(ISomeService someService)
{
_someService= someService;
_apiUtilities = new ApiUtilities();
}
[Route("api/page")]
public IHttpActionResult Get([FromBody]string url)
{
return _apiUtilities.TryCatch(() => _someService.Get(url));
}
}
Below is the update I've made based on a Friend's suggestion. I've removed the inheritance on the ApiController. I've also returned the same models the Ok, BadRequest and NotFound functions generate using the context of the current api.
public static class ApiUtilities
{
public static IHttpActionResult TryCatch(Action action, ApiController apiController)
{
try
{
if (apiController.ModelState.IsValid)
{
action();
return new OkResult(apiController);
}
else
{
return new BadRequestResult(apiController);
}
}
catch (Exception error)
{
return new NotFoundResult(apiController);
}
}
public static IHttpActionResult TryCatch<T>(Func<T> operation, ApiController apiController)
{
try
{
if (apiController.ModelState.IsValid)
{
var result = operation();
return new OkNegotiatedContentResult<T>(result, apiController);
}
else
{
return new BadRequestResult(apiController);
}
}
catch (Exception error)
{
return new NotFoundResult(apiController);
}
}
}

Why doesn't WebApi config for WebHook work on POST?

i want to get posted data from IFTTT from WebHook. It works when using GET but it doesn't when using POST.
[HttpPost]
[Route("InsertData")]
public IActionResult InsertData([FromBody] string FromAddress)
{
try
{
//var fromAddress = Request.Form["FromAddress"].ToString();
_webHookDb.UserData.Add(new UserData()
{
FromAddress = FromAddress,
DateTime = DateTime.Now
});
_webHookDb.SaveChanges();
return new JsonResult(FromAddress);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
Create a model to hold the data
public class Model {
public string FromAddress { get; set; }
}
use that in the action endoint.
[HttpPost]
[Route("InsertData")]
public async Task<IActionResult> InsertData([FromBody] Model model) {
try {
if(ModelState.IsValid) {
_webHookDb.UserData.Add(new UserData() {
FromAddress = model.FromAddress,
DateTime = DateTime.Now
});
await _webHookDb.SaveChangesAsync();
return new Ok(model);
}
return BadRequest(ModelState); //Bad data?
} catch (Exception ex) {
return StatusCode(500, ex.Message); //Something wrong with my code?
}
}
Review the message returned from the response in the web-hook to get details about why the request failed.
If HTTP Status Code 500 then something is wrong with how the data is being saved.
If HTTP Status Code 400 then something is wrong with how the data is being sent.

Can't access to my method delete of my api rest

I'm not able to access to my delete method of my api rest.
If i write the method like this it work:
[Route("api/Document/{documentId:int}")]
[HttpDelete]
public IHttpActionResult Delete([FromUri]int documentId,[FromBody] int [] documentsId)
{
try
{
documentCtrl = documentCtrl ?? new DocumentCtrl();
return Ok(documentCtrl.Delete(documentsId));
}
catch (DocumentNotFoundException)
{
return NotFound();
}
catch (Exception)
{
return InternalServerError();
}
}
It works, but if i put:
[Route("api/Document/MassiveDelete")]
[HttpDelete]
public IHttpActionResult MassiveDelete([FromBody] int[] ids)
{
try
{
documentCtrl = documentCtrl ?? new DocumentCtrl();
return Ok(documentCtrl.MassiveDelete(ids));
}
catch (DocumentNotFoundException)
{
return NotFound();
}
catch (Exception)
{
return InternalServerError();
}
}
I don't have acces, any ideas what could it be?
This is my request code:
DeleteDocument(id: number): Observable<boolean> {
return this._httpService.delete(AppModule.service + 'Document/' + id, AppModule.options)
.map((response: Response) => <boolean>response.json())
.catch(this.handleError);
}//This work if i want to delete one
DeleteDocuments2(ids:Array<number>):Observable<boolean>{
AppModule.options.body=ids;
return this._httpService.delete(AppModule.service + 'Document/MassiveDelete', AppModule.options)
.map((response: Response) => <boolean>response.json())
.catch(this.handleError);
}
You cannot send two parameters in your Api, you need to createa custom class like follow and send as follows,
MyCustomRequest {
public int[] documentIds;
public int documentId;
}
and then,
public IHttpActionResult MassiveDelete([FromBody] MyCustomRequest request)
you can access it as,
request.documentIds;
request.documentId;

Web API 2 Http Post Method

I am disgusted not have found a solution to this problem.
I started creating a new api using Web API 2 and just cannot get the POST and PUT to work. The Get all and Get single item works perfectly fine.
There are no related articles anywhere, and those that i've found relates only to Gets and Web API, but not Web API 2.
Any assistance would do please.
// POST: api/checkOuts
[HttpPost]
[ResponseType(typeof(checkOut))]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IHttpActionResult> PostcheckOut(checkOut co)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.checkOuts.Add(checkOut);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (checkOutExists(checkOut.id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = checkOut.id }, checkOut);
}
So basically, I'm just attempting to get a debug into the method.
Was especially disappointed in this link as it covered almost everything, but ai. http://www.asp.net/web-api/overview/web-api-routing-and-actions/create-a-rest-api-with-attribute-routing
Regards
This is a working code
// POST api/values
[HttpPost]
[ResponseType(typeof(CheckOut))]
public async Task<IHttpActionResult> Post([FromBody] CheckOut checkOut)
{
if (checkOut == null)
{
return BadRequest("Invalid passed data");
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.checkOuts.Add(checkOut);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (checkOutExists(checkOut.id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DefaultApi", new { id = checkOut.Id }, checkOut);
}
I've declared CheckOut class to be like this :
public class CheckOut
{
public int Id { get; set; }
public string Property2 { get; set; }
}
The Key things here are :
1- You need to add [FromBody] to your Api method.
2- I've tested it using Fiddler,
i- by choosing POST action.
ii- content-type: application/json.
iii- passing {"Id":1,"Property2":"Anything"} in the message body.
Hope that helps.

Categories

Resources