Keeping data between ActionResults - asp.net-mvc

I am populating a list based on data returned from a stored procedure, this first occurs in the SpecificArea ActionResult:
public ActionResult SpecificArea(ModelCallDetails call, int id = 0 )
{
ReturnSpecificAreas(call, id);
return PartialView("SpecificArea", listCallDetails);
}
When the list is displayed each row is an actionlink, which will sends the data to the SpecificAreaWorker:
[HttpGet]
public ActionResult SpecificAreaWorker(ModelCallDetails call, int id)
{
TempData["StringOfIds"] = StringOfIds;
ReturnSpecificAreas(call, id);
if (ResponseMessage == "Successful")
{
return PartialView("SpecificArea", listCallDetails);
}
else
{
return RedirectToAction("ViewCall");
}
}
I am wanting to collect the id of each row that is clicked and store them in a list in the model so that I can create a string of id's. However, each time a row in the table is clicked it refreshes the model, and I no longer have a list of ids anymore.
public void ReturnSpecificAreas(ModelCallDetails call, int id)
{
SelectedAffectedServiceID = id;
call.AffectedServiceList.Add(SelectedAffectedServiceID);
foreach (int item in call.AffectedServiceList)
{
if (TempData["StringOfIds"] != null)
{
StringOfIds = TempData["StringOfIds"].ToString();
StringOfIds += string.Join(",", call.AffectedServiceList.ToArray());
}
else
{
StringOfIds += string.Join(",", call.AffectedServiceList.ToArray());
}
}
I have tried to mantain the data in tempdata, but can't manage to execute this -will the tempdata refresh each time the actionlink is clicked? Is there a better way to achieve this?

I believe you are using MVC5? If so, use
System.Web.HttpContext
This gets current request
to save....
System.Web.HttpContext.Current.Application["StringOfIds"] = StringOfIds; //Saves global
System.Web.HttpContext.Current.Session["StringOfIds"] = StringOfIds; //Saves Session
To retrieve...
StringOfIds = (string) System.Web.HttpContext.Current.Application ["StringOfIds"]; //Retrieves from global memory
StringOfIds = (string) System.Web.HttpContext.Current.Session ["StringOfIds"]; //retrieves from session memory
Good luck.

Related

ASP.NET CORE MVC Loading table page takes too long

I have a table with orders(around 8000 records),
The table takes a few seconds to load.
The reason for that is because one of the field shown on the page is being retrieved from another table
(returnProductName).
when removing this function the table loads fast.
When loading the records I'm using Skip and Take but when retrieving the product name i'm iterating all the Orders since if the user wants to search by product name it will show all results with this product.
The product table is not big (around 70 records)
I can't figure out why the function will make the page load so slow.
I know i can just add the product name column to the table and populate it when ever adding new orders,
but this doesn't sounds right,
Can anyone tell me the reason for this delay?
returnProductName Function :
public string returnProductName(int productId)
{
return (_unitOfWork.Product.GetAll().Where(q => q.Id == productId).Select(q =>
q.ProductName)).FirstOrDefault();
}
Function that loads the page data:
[HttpPost]
public ActionResult GetList()
{
//Server Side parameters
int start = Convert.ToInt32(Request.Form["start"].FirstOrDefault());
int length = Convert.ToInt32(Request.Form["length"].FirstOrDefault());
string searchValue = Request.Form["search[value]"].FirstOrDefault();
string sortColumnName = Request.Form["columns["+Request.Form["order[0][column]"]+"][name]"].FirstOrDefault();
string sortDirection = Request.Form["order[0][dir]"].FirstOrDefault();
List<Order> orderList = new List<Order>();
orderList = _unitOfWork.Order.GetAll().ToList();//Working Fast
int totalRows = orderList.Count;
foreach (Order order in orderList)
{
order.ProductName = returnProductName(order.ProductId);
}
if (!string.IsNullOrEmpty(searchValue))
{
orderList = orderList.Where(x => x.FullAddress.ToLower().Contains(searchValue.ToLower())
x.Id.ToString().Contains(searchValue.ToLower()) ||
x.OrderStatus.ToLower().Contains(searchValue.ToLower()) ||
x.ProductName.ToLower().Contains(searchValue.ToLower()) |||
x.Quantity.ToString().Contains(searchValue.ToLower()) ||
x.Cost.ToString().Contains(searchValue.ToLower()) ||
(!string.IsNullOrEmpty(x.TrackingNumber) && x.TrackingNumber.ToString().Contains(searchValue.ToLower()))
).ToList<Order>();
}
int totalRowsAfterFiltering = orderList.Count;
orderList = orderList.Skip(start).Take(length).ToList<Order>();
return Json(new { data = orderList, draw = Request.Form["draw"], recordsTotal = totalRows ,
recordsFiltered = totalRowsAfterFiltering});
}
I would perhaps consider updating the GetAll() method or creating another one which returns a dictionary.
In this case GetAllById() and then updating returnProductName which I would rename to GetProductName():
// Or whatever your type is
public Dictionary<int, List<Product>> GetAllById()
{
// your code..
return data
.GroupBy(x => x.Id)
.ToDictionary(x => x.Key, x => x.ToList());
}
public string GetProductName(int productId)
{
var products = _unitOfWork.Product.GetAllById();
return products[productId].FirstOrDefault(q => q.ProductName);
}

am working on updating a single attribute in the User Model which is the balance attribute,

how I can update a single value for an already existing row in the db by only having a parameters that I want to add it to this attribute
here is my code for a trivial way but didnt work
public bool BuyBook(int BookId, int UserId, int BookPrice){
using (var ctx = new OnlineBooksEntities())
{
User updatedCustomer = (from c in ctx.Users
where c.UserId == UserId
select c).FirstOrDefault();
updatedCustomer.Balance = BookPrice;
ctx.SaveChanges();
}
this.DeleteBook(BookId);
return true;
}
Add an sql query to the method solves the update aim
public bool BuyBook(int BookId, int UserId, int BookPrice)
{
try
{
using (var ctx = new OnlineBooksEntities())
{
User user = ctx.Users.Where(x => x.UserId == UserId).FirstOrDefault();
BookPrice = (int)user.Balance + BookPrice;
int noOfRowUpdated =
ctx.Database.ExecuteSqlCommand("Update Users set Balance = "+BookPrice+ " where UserId ="+UserId);
}
Updating basically means changing an existing row's value. Since you mentioned EF, you can do this by retrieving the object, changing its value, and saving it back. Thus you can do something like this:
using (var db = new MyContextDB())
{
var result = db.Books.SingleOrDefault(b => b.BookPrice == bookPrice);
if (result != null)
{
result.SomeValue = "Your new value here";
db.SaveChanges();
}
}

Adding child entity works locally but not on live Azure site

I have a web app controller action that is creating a child entity. I have a Location model with a LocationPic collection. I'm trying to add a new LocationPic to an existing Location. Locally this works fine, but when I run it on Azure the LocationPic gets created but doesn't reference the Location. So I end up with an orphaned LocationPic that doesn't know what Location it belongs to.
Also, it works fine locally and on Azure for Locations that ALREADY have pics (I have a separate API controller that seems to work fine). So I can add new pics to Locations that already have some. But I can't add new pics to a new Location that doesn't have any pics.
Here's my controller action:
// POST: Pictures/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("LocationID,Name,Description")] LocationPic locationPic, IFormFile image)
{
var location = await _context.Locations.FindAsync(locationPic.LocationID);
if (location == null)
{
return NotFound();
}
Trace.TraceInformation($"Create Pic for location[{location.LocationID}:{location.LocationName}]");
_userService = new UserService((ClaimsIdentity)User.Identity, _context);
if (!_userService.IsAdmin)
{
return Unauthorized();
}
if (image == null)
{
return BadRequest();
}
if (String.IsNullOrEmpty(locationPic.Name))
{
locationPic.Name = image.FileName;
}
var helper = new AzureTools();
var filename = await helper.GetFileName(image);
locationPic.FileName = filename;
//Added this to try to force LocationPics to be initialized
if (location.LocationPics == null)
{
Trace.TraceInformation("location.LocationPics is null");
location.LocationPics = new List<LocationPic>();
}
else
{
Trace.TraceInformation($"location.LocationPics count == {location.LocationPics.Count}");
}
if (ModelState.IsValid)
{
Trace.TraceInformation($"Location pic valid: [{locationPic.LocationID}] {locationPic.Name}");
//Overly-explicit attempts to get entities linked
locationPic.Location = location;
location.LocationPics.Add(locationPic);
//I've tried _context.LocationPics.Add as well and seemingly no difference
_context.Add(locationPic);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Edit), new { locationID = location.LocationID });
} else
{
Trace.TraceInformation("Invalid model state");
return BadRequest();
}
}
All the right information seems to be coming into my parameters from my form properly, and the LocationPic gets created fine. It just isn't linked to the Location, despite the "LocationID" showing properly before being saved. Plus, I get the proper redirect back to the Edit action, not a BadRequest or anything.
Locally, the only difference I've noticed is that a Location with no pics has a LocationPics that is an empty collection with Count==0. On Azure, a Location with no pics seems to have a LocationPics that is null. So that's why I tried to add the bit that initializes it if it's null, though my API controller that works didn't need to do anything of the sort.
Here's my (working) API Controller action, for reference:
[HttpPost("{id}", Name = "PostPicture")]
public async Task<IActionResult> PostPicture([FromRoute] int id, IFormFile image, string Name, string Description)
{
var location = _context.Locations.Find(id);
if (location == null)
{
return NotFound();
}
_userService = new UserService((ClaimsIdentity)User.Identity, _context);
if (!_userService.IsCustomer(location.CustomerID))
{
return Unauthorized();
}
if (image == null)
{
return BadRequest();
}
if (String.IsNullOrEmpty(Name))
{
Name = image.FileName;
}
var helper = new AzureTools();
var filename = await helper.GetFileName(image);
var locationPic = new LocationPic
{
Name = Name,
FileName = filename,
Description = Description,
Location = location
};
_context.LocationPics.Add(locationPic);
_context.SaveChanges();
return Ok(filename);
}
Turns out the location pics were going in fine, but in my controller actions that I was displaying them I wasn't using .Include() to include the LocationPics collection? Except that it worked for Locations that previously had location pics? And it worked locally totally fine for everything. So I'm not entirely sure.
As soon as I put .Include() to include the LocationPics collection in my "EditByLocation" controller action that grabs a location by ID and sends that to the view that displays the location and pics for that location, all the previous pics I'd added popped into view on the locations that I thought weren't getting them. So for some reason, Location A (with pics) was displaying its pics fine, but Location B (apparently with pics but I couldn't see them until now) wasn't displaying them. I had no reason to believe that the locations weren't displaying correctly because some were displaying fine, and others came over with a null LocationPics collection. So that makes no sense to me. But it seems to be working now.
For completeness, here is my final controller action code, which you may notice looks very similar to the API controller action, which is what it looked like to start with:
// POST: Pictures/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("LocationID,Name,Description")] LocationPic locationPic, IFormFile image)
{
var location = await _context.Locations.FindAsync(locationPic.LocationID);
if (location == null)
{
return NotFound();
}
_userService = new UserService((ClaimsIdentity)User.Identity, _context);
if (!_userService.IsAdmin)
{
return Unauthorized();
}
if (image == null)
{
return BadRequest();
}
if (String.IsNullOrEmpty(locationPic.Name))
{
locationPic.Name = image.FileName;
}
var helper = new AzureTools();
var filename = await helper.GetFileName(image);
locationPic.FileName = filename;
if (ModelState.IsValid)
{
locationPic.Location = location;
_context.LocationPics.Add(locationPic);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(EditByLocation), new { locationID = location.LocationID });
}
return View("EditByLocation", location);
}
And here's my new EditByLocation controller action:
// GET: Pictures/EditByLocation?locationID=5
public async Task<IActionResult> EditByLocation(int locationID)
{
_userService = new UserService((ClaimsIdentity)User.Identity, _context);
if (!_userService.IsAdmin)
{
return Unauthorized();
}
ViewData["ImageBaseUrl"] = _config["AzureStorage:Url"] + "/" + _config["AzureStorage:Folder"];
var location = await _context.Locations.Include(l => l.LocationPics).FirstOrDefaultAsync(l => l.LocationID == locationID);
if (location == null)
{
return NotFound();
}
if (location.LocationPics == null)
{
location.LocationPics = new List<LocationPic>();
}
return View("EditByLocation", location);
}
The only real change above was adding the .Include(l => l.LocationPics). So again, no idea why it worked locally without it, and why SOME locations worked on Azure without it but not others.

'System.Linq.IQueryable<NewsSite.Models.Domain.Tbl_News>' does not contain a definition

I Create A News Site With MVC5 But I Have Problem .
in Model i Create A Repository Folder And in this i Create Rep_Setting for
Connect to Tbl_Setting in DataBase .
public class Rep_Setting
{
DataBase db = new DataBase();
public Tbl_Setting Tools()
{
try
{
var qGetSetting = (from a in db.Tbl_Setting
select a).FirstOrDefault();
return qGetSetting;
}
catch (Exception)
{
return null;
}
}
}
And i Create a Rep_News for Main Page .
DataBase db = new DataBase();
Rep_Setting RSetting = new Rep_Setting();
public List<Tbl_News> GetNews()
{
try
{
List<Tbl_News> qGetNews = (from a in db.Tbl_News
where a.Type.Equals("News")
select a).OrderByDescending(s => s.ID).Skip(0).Take(RSetting.Tools().CountNewsInPage).ToList();
return qGetNews;
}
catch (Exception ex)
{
return null;
}
}
But This Code Have Error to Me
OrderByDescending(s=>s.ID).Skip(0).Take(RSetting.Tools().CountNewsInPage).ToList();
Error :
Error 18 'System.Linq.IQueryable<NewsSite.Models.Domain.Tbl_News>' does
not contain a definition for 'Take' and the best extension method overload
'System.Linq.Queryable.Take<TSource>(System.Linq.IQueryable<TSource>, int)' has
some invalid arguments
E:\MyProject\NewsSite\NewsSite\Models\Repository\Rep_News.cs 50 52 NewsSite
How i Resolve it ?
Try it this way. The plan of debugging is to split your execution, this also makes for a more reusable method in many cases. And a good idea is to avoid using null and nullables if you can, if you use them "on purpose" the you must have a plan for them.
DataBase db = new DataBase();
Rep_Setting RSetting = new Rep_Setting();
public List<Tbl_News> GetNews()
{
int skip = 0;
Tbl_Setting tools = RSetting.Tools();
if(tools == null){ throw new Exception("Found no rows in the database table Tbl_Setting"); }
int? take = tools.CountNewsInPage;//Nullable
if(!take.HasValue)
{
// Do you want to do something if its null maybe set it to 0 and not null
take = 0;
}
string typeStr = "News";
List<Tbl_News> qGetNews = (from a in db.Tbl_News
where a.Type.Equals(typeStr)
select a).OrderByDescending(s => s.ID).Skip(skip).Take(take.Value);
return qGetNews.ToList();
}
if qGetNews is a empty list you now don't break everything after trying to iterate on it, like your return null would. instead if returning null for a lit return a new List<>() instead, gives you a more resilient result.
So I said reusable method, its more like a single action. So you work it around to this. Now you have something really reusable.
public List<Tbl_News> GetNews(string typeStr, int take, int skip = 0)
{
List<Tbl_News> qGetNews = (from a in db.Tbl_News
where a.Type.Equals(typeStr)
select a).OrderByDescending(s => s.ID).Skip(skip).Take(take);
return qGetNews.ToList();
}
Infact you shjould always try to avoid returning null if you can.
public class Rep_Setting
{
DataBase db = new DataBase();
public Tbl_Setting Tools()
{
var qGetSetting = (from a in db.Tbl_Setting
select a).FirstOrDefault();
if(qGetSetting == null){ throw new Exception("Found no rows in the database table Tbl_Setting"); }
return qGetSetting;
}
}

How to pass value from one action to another action having same controller

Hi I am developing an application in MVC3. and i am stuck at one place. Everytime when control goes to IIndex1 action its argument value has become 0. But it should be same as value in IIndex action argument. I have used session, ViewBag, ViewData but my problem is remains. Please suggest me.
public ActionResult GetMDN(string msisdn)
{
number = msisdn.Substring(0, msisdn.IndexOf('$'));
if (number.ToLower() != "unknown" && number.Length == 12)
{
number = number.Remove(0, 2);
}
Session["msdresponse"] = number;
Session["moptr"] = msisdn.Substring(msisdn.LastIndexOf('$') + 1);
number = msisdn;
int sngid=int.Parse(ViewData["isongid"].ToString());
return RedirectToAction("IIndex1", new { iid = sngid });
}
public ActionResult IIndex(int id)
{
ViewBag.isongid = id;
ViewData["isongid"] = id;
Response.Redirect("http:XXXXXXXXXXXXXXXX");
return RedirectToAction("GetMDN");
}
public ActionResult IIndex1(int iid)
{
}
You can use TempData.You can pass every types of data between to action, whether they are in same controller or not. Your code should be something like it:
public ActionResult GetMDN(string msisdn)
{
int sngid=10;
TempData["ID"] = sngid;
return RedirectToAction("IIndex");
}
public ActionResult IIndex()
{
int id = Convert.ToInt32(TempData["ID"]);// id will be 10;
}
Use TempData instead of ViewData/ViewBag to store data that should persist after redirect.
ViewData/ViewBag allow to pass value from controller to view.
Something to read on this subject:
http://www.codeproject.com/Articles/476967/WhatplusisplusViewData-cplusViewBagplusandplusTem
http://msdn.microsoft.com/en-us/library/dd394711(v=vs.100).aspx
you can use TempData["name"] = variableToPass;

Resources