Ajax calls are slower to controller in physically different file - c#

While trying to speed up some ajax calls in one of our MVC pages, I encountered some strange behavior which I can't really explain. I have some ajax calls being made every N seconds for polling some statistics.
It seems like ajax calls being made to a controller in a physically different file are substantially slower than similar calls being made to a controller in the same physical file as where the view originates from.
See my simplified examples:
Situation 1: Only 1 file
FooController.cs
namespace FooBar.Areas.FooArea.Controllers
{
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class FooTestController: _BaseController
{
public JsonResult GetStats()
{
try
{
var req = new RestRequest() { Method = Method.GET };
req.AddHeader("Content-Type", "application/json");
req.AddHeader("Accept", "application/json");
req.AddParameter("apikey", /*APIKEY*/);
var client = new RestClient(/*STATSURL*/);
var response = client.Execute(req);
if (response.StatusCode == HttpStatusCode.OK)
return Json(new { success = true, content = response.Content });
else
return Json(new { success = false });
}
catch
{
return Json(new { success = false });
}
}
public JsonResult GetAgents()
{
var req = new RestRequest() { Method = Method.GET };
req.AddHeader("Content-Type", "application/json");
req.AddHeader("Accept", "application/json");
req.AddParameter("apikey", /*APIKEY*/);
try
{
var client = new RestClient(/*AGENTSURL*/);
var response = client.Execute(req);
if (response.StatusCode == HttpStatusCode.OK)
return Json(new { success = true, content = response.Content });
else
return Json(new { success = false });
}
catch
{
return Json(new { success = false });
}
}
}
public class FooController : _BaseController
{
// VIEW OF THE PAGE MAKING THE AJAX REQUESTS
public ActionResult Index()
{
Title = "Home";
return View();
}
}
}
Situation 2: 2 seperate files in same folder
FooController.cs
namespace FooBar.Areas.FooArea.Controllers
{
public class FooController: _BaseController
{
// VIEW OF THE PAGE MAKING THE AJAX REQUESTS
public ActionResult Index()
{
Title = "Home";
return View();
}
}
}
FooAjaxController.cs
namespace FooBar.Areas.FooArea.Controllers
{
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class FooAjaxController: _BaseController
{
public JsonResult GetStats()
{
try
{
var req = new RestRequest() { Method = Method.GET };
req.AddHeader("Content-Type", "application/json");
req.AddHeader("Accept", "application/json");
req.AddParameter("apikey", /*APIKEY*/);
var client = new RestClient(/*STATSURL*/);
var response = client.Execute(req);
if (response.StatusCode == HttpStatusCode.OK)
return Json(new { success = true, content = response.Content });
else
return Json(new { success = false });
}
catch
{
return Json(new { success = false });
}
}
public JsonResult GetAgents()
{
var req = new RestRequest() { Method = Method.GET };
req.AddHeader("Content-Type", "application/json");
req.AddHeader("Accept", "application/json");
req.AddParameter("apikey", /*APIKEY*/);
try
{
var client = new RestClient(/*AGENTSURL*/);
var response = client.Execute(req);
if (response.StatusCode == HttpStatusCode.OK)
return Json(new { success = true, content = response.Content });
else
return Json(new { success = false });
}
catch
{
return Json(new { success = false });
}
}
}
}
In both situations, the ajax calls are made from jQuery as follows:
jQuery
$.ajax({
url: // URL TO ACTION DEPENDING ON SITUATION,
type: "POST",
dataType: "json",
cache: false,
success: function (result)
{
if (result.success)
{
var content = JSON.parse(result.content);
console.log(content);
}
}
});
Now, the response times from the ajax requests for both situations are as follows, with situation 1 being displayed on the left and situation 2 on the right:
So, as you can see the average time of a call made to GetStats() and GetAgents() in situation 1 is 52.8 ms and 53.8 ms respectively.
However, in situation 2, the average time of the calls is 486.8 ms and 529.9 ms.
My question now is: how can it be that ajax calls made to actions are on average almost 10 times slower when those actions reside in a controller in a physically different file, than when those actions reside in a controller which shares the same physical file as the file rendering the view in the first place?
Is it because the file containing the action to render the view is already loaded and kept in memory, while the seperate file, as in in situation 2, is opened and closed every time the action is called? Or is something more sinister going on?

You shared most of the code but not _BaseController.cs
On each request, MVC gets the controller using dependency injection.
[speculation] MVC may store the most recently-used controller, returning the same one several times. However if you are switching controllers, perhaps it creates a new one each time.
Perhaps there is some very slow code in the _BaseController default constructor - perhaps database queries. It sounds unlikely but has happened in my experience.
Taken together, these things would cause the slowdown you describe.

Related

how to find where & why is server taking too much time to executing the controller function?

i am new to .net environment . I am trying to upload multiple files through UI controller to some other api. it like from UI Js ajax request to asp.net UI Controller to some other api actual function. when i am uploading one it is uploading . but when is upload multiple files it taking too much time. most of files size are 3 MB.
i am really confuse where or how to know where in IIS taking too much time. because when make direct call to api from js it uploading files very fast but when it went through UI controller it is taking too much time.
i am adding UI Controller code. i
public async Task<JsonResult> DataList()
{
var client = GetHttpClientWithBearerToken();
client.Timeout = TimeSpan.FromSeconds(300);
var url = "some url";
string Rec;
string File;
try
{
var requestContent = new MultipartFormDataContent();
requestContent.Add(new StringContent(HttpContext.Request.Form["Id"]), "Id");
int FilesCount = HttpContext.Request.Files.Count;
for (int i = 0; i < FilesCount; i++)
{
HttpPostedFileBase file = HttpContext.Request.Files[i];
requestContent.Add(new StreamContent(file.InputStream), "file[]", file.FileName);
}
HttpResponseMessage responseMessage = await client.PostAsync(url, requestContent);
if (responseMessage.IsSuccessStatusCode)
{
var returnData = responseMessage.Content.ReadAsStringAsync().Result;
var jsonResult = Json(new { success = true, Content = returnData }, JsonRequestBehavior.AllowGet);
return jsonResult;
}
else
{
return Json(new { success = false, Content = "-1" }, JsonRequestBehavior.AllowGet);
}
}
catch (Exception e)
{
Logging.WriteLog(e.Message, Logging.LogMsgType.General);
}
return Json(new { success = false, Content = "-1" }, JsonRequestBehavior.AllowGet);
}`
have added some logs but there is not high time in the log so request is taking so much time to reach there. till the time chrome network tab showing

Passing potential huge files in chunks to Web API

I have to pass potential huge files from an ASP.NET Core middle Server to an ASP.NET backend.
I can’t use the ASP.NET backend web API directly, I have to go over a MVC Controller.
Currently my middle Server gets the file in Chunks (and does some verification), saves it to disk and after it completes it rereads it in chunks to pass it forward.
Is there an easy way to pass the chunks without buffering the file?
I currently got this:
MVC Controler:
[HttpPost]
public ActionResult UploadChunk(IFormFile fileChunk, string chunkMetadata)
{
if (!string.IsNullOrEmpty(chunkMetadata))
{
var metaDataObject = JsonConvert.DeserializeObject<ChunkMetadata>(chunkMetadata);
...
AppendContentToFile(tempFilePath, fileChunk); //writes file with FileMode.Append,
}
}
my upload to back end [Edit]:
public IHttpActionResult FileUpload(string fileUri)
{
try
{
if (Request.Content.IsMimeMultipartContent())
{
var configProvider = Resolve<IApplicationConfigurationProvider>();
var uploadRootDir = configProvider.TemporaryFileUploadPath;
var streamProvider = new MultipartStreamProvider(uploadRootDir);
// If the file is huge and is not split into chunks, the 'ReadAsMultipartAsync' call
// takes as long as full file has been copied
var readResult = Request.Content.ReadAsMultipartAsync(streamProvider).Result;
var fileSvc = Resolve<IFileService>();
string targetFilePath = string.Empty;
foreach (MultipartFileData fileData in streamProvider.FileData)
{
ContentDispositionHeaderValue contentDisposition = fileData.Headers.ContentDisposition;
string fileName = contentDisposition.FileName;
if (!GetFileName(fileName, out var targetFileName))
{
return BadRequest($"ContentDisposition.FileName must match 'file' of URI-query! Actual: {targetFileName}");
}
var rawSourceFileInfo = new FileInfo(targetFileName);
if (contentDisposition.Size.HasValue && contentDisposition.Size.Value > 0)
{
if (!fileSvc.CreateNewFilePath(fileUri, new PathOptions(true), out var validFileFullPath))
{
return BadRequest($"Unable to create physical-path from fileId='{fileUri}'");
}
targetFilePath = validFileFullPath.FullName;
fileSvc.AddChunk(validFileFullPath.FullName, contentDisposition.Size.Value, fileData.LocalFileName);
}
else
{
return BadRequest("File upload must set a valid file-length in ContentDisposition");
}
}
return Ok(targetFilePath);
}
else
{
return BadRequest("File upload must be a 'IsMimeMultipartContent'");
}
}
catch (Exception error)
{
LogError(error, "FileUpload");
return InternalServerError(error);
}
}
Thanks in advance for any help!
[Edit]
my not working call from client to back end:
<script>
$(document).ready(function (e) {
$("#uploadImage").on('change', (function (e) {
// append file input to form data
var fileInput = document.getElementById('uploadImage');
var file = fileInput.files[0];
var formData = new FormData();
formData.append('uploadImage', file);
$.ajax({
url: "http://localhost/service/filestore/v1/upload?fileUri=someText",
type: "POST",
data: formData,
contentType: false,
cache: false,
processData: false,
success: function (data) {
if (data == 'invalid') {
// invalid file format.
$("#err").html("Invalid File !").fadeIn();
}
else {
// view uploaded file.
$("#preview").html(data).fadeIn();
$("#form")[0].reset();
}
},
error: function (e) {
$("#err").html(e).fadeIn();
}
});
}));
});
</script>

How to get single list item from response message in web api

I am new to web api. I have a method, that returns 3 objects from response message. I want to get specific object from the response message,
public HttpResponseMessage GetAllStudents(HttpReqestMessage request)
{
HttpResponseMessage response = null;
return CreateHttpResponse(request, () =>
{
// some logics here
response = request.CreateResponse(HttpStatusCode = OK, new {success = true, StudentName, ListOfStudents, ListOfSubjects});
return response;
});
}
In this above code i want to get the ListOfStudents object alone from the response message. Please anyone help me to get this.
I think you have a malformatted json , you should create a property for each list, check the next example :
public HttpResponseMessage GetAllStudents(HttpReqestMessage request)
{
HttpResponseMessage response = null;
return CreateHttpResponse(request, () =>
{
// some logics here
response = request.CreateResponse(HttpStatusCode = OK, new {success = true, studentName = StudentName, listOfStudents = ListOfStudents, listOfSubjects = ListOfSubjects});
return response;
});
}
Example getting this with with jquery
$.get("GetAllStudents", function(data) {
if (data.success)
{
console.log(data.listOfStudents);
}
});

MVC Parameter Binding from JSON into multiple parameters

I'm receiving JSON data from this method:
function CallAPI(controllerName, functionName, sendData, callback) {
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function () {
if (ajax.readyState == 4) {
if (ajax.status == 200) {
var response = JSON.parse(ajax.responseText);
callback(response);
}
else {
callback();
}
}
}
ajax.open("POST", "http://" + window.location.host + "/API/" + controllerName + "/" + functionName, true);
ajax.setRequestHeader('Content-Type', 'application/json');
ajax.send(JSON.stringify(sendData));
}
function someRequest() {
var data = {};
data.userID = 1;
data.someValue = 20;
CallAPI("Home", "Add", data, function (response) {
if(response) {
alert(response.Message); //This property always exist
}
else {
alert("internet/connection error.");
}
});
}
And my Controller is:
[System.Web.Http.HttpPost]
public HttpResponseMessage Add(int userID, int someValue)
{
//Here!
return Request.CreateResponse(HttpStatusCode.OK, new GenericResponseAPI() { Status = false, Message = "Generic Message." });
}
I can create a model and MVC would bind it accordingly, but I have so many of these requests that I don't want to create various models (or a big one containing all of the properties), but instead have these simple functions with basic type parameters like this.
What do I need to do to be able to work with the controller function above, without changing the content-type of the incoming message?
Edit: I found a workaround with this:
[System.Web.Http.HttpPost]
public HttpResponseMessage Add(JObject jObject)
{
int userID = (int)jObject.getValue("userID").ToObject(typeof(Int32));
int someValue = (int)jObject.getValue("someValue").ToObject(typeof(Int32));
return Request.CreateResponse(HttpStatusCode.OK);
}
I imagine the reason is that your function signature is:
function CallAPI(controllerName, functionName, sendData, callback)
and you're calling
CallAPI("Home", "Add", function (response) { ... })
So you're never actually sending data in the format MVC is expecting.
I'd double check this by using something like Fiddler and by debugging your JavaScript code using your browsers dev-tools.

When invoked with a jQuery $.ajax, what to return from the Action Method if things go ok?

Here's my code:
[HttpPost]
public ActionResult VoteChampionStrongAgainst(string championStrong, string againstChampion)
{
int champStrongId = int.Parse(championStrong);
int againstChampId = int.Parse(againstChampion);
string ip = System.Web.HttpContext.Current.Request.UserHostAddress;
using (EfCounterPickRepository counterPickRepository = new EfCounterPickRepository())
{
var existingCounterPick = counterPickRepository.FindAll()
.SingleOrDefault(x => x.ChampionStrong == champStrongId && x.AgainstChampion == againstChampId);
//Does this counterpick combination already exist?
if (existingCounterPick != null)
{
//Has this user already voted?
var existingVote = counterPickRepository.FindVoteForUser(ip, existingCounterPick.CounterPickVoteId);
//He hasn't voted, add his vote history.
if (existingVote == null)
{
StrongCounterHistory history = new StrongCounterHistory();
history.IPAddress = ip;
history.VoteType = true;
history.StrongCounterPickVoteId = existingCounterPick.CounterPickVoteId;
counterPickRepository.AddStrongPickHistory(history);
counterPickRepository.SaveChanges();
//Add a total vote the pick.
existingCounterPick.TotalVotes++;
counterPickRepository.SaveChanges();
}
else
{
//Will use this to display an error message.
//How to return an "error" that jquery understands?
}
}
else //This combination doesn't exist. Create it.
{
//Create it....
StrongCounterPickVote newCounterPick = new StrongCounterPickVote();
newCounterPick.ChampionStrong = champStrongId;
newCounterPick.AgainstChampion = againstChampId;
newCounterPick.TotalVotes = 1;
counterPickRepository.CreateNewCounterPick(newCounterPick);
counterPickRepository.SaveChanges();
//Assign a vote history for that user.
StrongCounterHistory history = new StrongCounterHistory();
history.IPAddress = ip;
history.VoteType = true;
history.StrongCounterPickVoteId = newCounterPick.CounterPickVoteId;
counterPickRepository.AddStrongPickHistory(history);
counterPickRepository.SaveChanges();
}
return View();
}
}
Here's my jQuery code:
$(".pick .data .actions .btn-success").click(function () {
var champStrongId = $(this).data("champstrongid");
var againstChampId = $(this).data("againstchampid");
$.ajax({
type: 'POST',
url: "/Counterpicks/VoteChampionStrongAgainst",
data: { championStrong: champStrongId, againstChampion: againstChampId },
success: function () {
alert("Great success!");
},
error: function (e) {
alert("Something bad happened!");
console.log(e);
}
});
});
What do I need to return from my ActionMethod so the code execution enters success: if things went OK, or error: if things go wrong (for example, he already voted on this particular counter pick?
Servlet should answer a "200 OK" HTTP response.
Don't know about your 'View' api, but HttpServletResponse.setStatus(200) would do on the Java side. Don't forget, you can request the AJAX url manually in your browser to see what it is returning..
Here's some things I'd do...
public JsonResult VoteChampionStrongAgainst(string championStrong, string againstChampion) {
var success = true;
// Do all of your data stuff
return Json(new { success = success, error = 'Some error message'});
}
The JsonResult is a special ActionResult for returning Json. It automatically sets the correct headers for the browser. The Json() will use ASP.NET's built in serializer to serialize an anonymous object to return to the client.
Then with your jQuery code...
$.ajax({
type: 'POST',
url: "/Counterpicks/VoteChampionStrongAgainst",
data: { championStrong: champStrongId, againstChampion: againstChampId },
success: function (json) {
if (json.success) {
alert("Great success!");
}
else if(json.error && json.error.length) {
alert(json.error);
}
},
// This error is only for responses with codes other than a
// 200 back from the server.
error: function (e) {
alert("Something bad happened!");
console.log(e);
}
});
In order to have the error fire you'd have to return a different response code with Response.StatusCode = (int)HttpStatusCode.BadRequest;
You can return 500 internal server error if there are any errors on your server something like
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
Response.ContentType = "text/plain";
return Json(new { "internal error message"});

Categories