In my controller, the method that returns the View also initializes a few values for some class-level properties:
private string igc = String.Empty;
private string igcCode = String.Empty;
private bool isSuggested = false;
public ActionResult Codes(Codes objCodes)
{
try
{
FillDropDowns(objCodes);
igc = String.Empty;
if (objICDCodes.FromWhere.IndexOf("MedicalInfo-Suggested") >= 0)
{
igc = objCodes.FromWhere.Remove(0, "MedicalInfo-Suggested-".Length);
igcCode = igc.Substring(0, igc.IndexOf("-")).Trim();
objCodes.ICGCode = igcCode;
isSuggested = true;
}
}
catch (Exception ex)
{
//logging error
ElmahLogUtility.ErrorException(ex);
}
return View(base.GetViewPath("Codes"), objCodes);
}
Additionally, there is this method which gets called to bind data to a grid on the page:
public JsonResult GetSelectedCodesInfo(List<SearchField> searchFields, GridDataSourceRequest request)
{
//creating the instance of DataSourceResult.
DataSourceResult dataSourceResult = null;
try
{
// Creating the instance of CommonBLL to load the values.
CommonBLL objCommonBLL = new CommonBLL();
if (isSuggested)
{
searchFields.Add(new SearchField() { ElementName = "aIGCode", Value = igcCode });
searchFields.Add(new SearchField() { ElementName = "aFor", Value = "EtiologicDiagnosis" });
}
// Getting the Codes information and storing in the DataSource Result.
dataSourceResult = objCommonBLL.GetSelectedCodesInfo(searchFields, request);
}
catch (Exception ex)
{
//Logging the Exception
ElmahLogUtility.ErrorException(ex);
}
// Returning the Result.
return Json(dataSourceResult, JsonRequestBehavior.AllowGet);
}
isSuggested gets set to true when the View is created, but when the data is bound to the grid isSuggested is set to false for some reason.
My grid is defined in a Razor view like so:
#Html.Grid("CodesSelectionGrid").ReadSource("Controller", "GetSelectedCodesInfo").OnGridDataBound("AssignCodeValues").Lazyload(true).EnableGrouping(false).EnableSorting(true).PageSize(10).Height("390px").Width("610px").EnablePaging(true).EnableFiltering(false).EnableMultiSelect(true).SelectionMode(SelectionMode.Single, "GetSelectedCodeDetails").RowSelection(GridRowSelection.None).ShowToolBar(true).SelectionCSSClass("icd-editable-cell").PageButtonCount(3)
That .ReadSource("Controller", "GetSelectedCodesInfo") bit is what refers to the Controller and the method on the controller to call. So, it's calling the second snippet of code above.
I must be accessing two separate instances of my Controller class, but I do not know how to solve this problem. How can I do this? How could I have my grid pass a reference of the Codes object? Then I could just get the values from there for the grid...
This is the expected behavior. isSuggested is a class level variable. Every time you make an Http request, a new instance of your controller will be created. That means the variable will be initialized to false. Remember, Http is Stateless :)
If you want to persist a variable value between multiple http calls, you need to persist it. You have different options like
Persist to a database table and read from that in the second call
Write to a file in disk and read from that in the second call
Save to user session and read from that in the second call
Related
Working on a Blazor server side project. I am using Blazored Local Storage for this: https://github.com/Blazored/LocalStorage
I have a dropdown, when a value is selected I want to store the value in local storage so that when the user returns to the page later the dropdown will be at the same value.
To accomplish this I bound the dropdown to the value _store and added a getter and setter. This part works:
private int _store {
get { return GetStoreValue(); }
set { SetStoreValue(value); }
}
Where it falls apart is the two functions for getting and setting:
private int GetStoreValue()
{
var returnValue = 0;
string storedValue = _storage.GetItemAsync<string>("UserListSelectedStore").ToString();
if (!string.IsNullOrEmpty(storedValue))
{
int storedValueInt = Int32.Parse(storedValue);
if (storedValueInt > 0)
{
returnValue = storedValueInt;
}
else
{
returnValue = _storeValue;
}
}
else
{
returnValue = _storeValue;
}
return returnValue;
}
private void SetStoreValue(int value)
{
_storage.SetItemAsStringAsync("UserListSelectedStore", value.ToString());
_storeValue = value;
}
Essentially, if there is a stored store value, use that. Otherwise return whatever was set when the component was initialized.
The problem - The value of storedValue is always an empty string "". It appears to write the value to storage correctly, but has problems reading it. Since this is a server side project, I have to use async for reading, I am not sure if that is the issue but I tried blindly changing it a few times and couldn't get it to work.
How do I get Blazor to read the stored value and return it if it exists?
Figured it out. I needed to add it to the OnInitializedAsync() method so everything gets set before the DOM loads.
I've got a controller action that is using TempData to get complex objects from another action. The issue happens when the user refreshes the page and gets null object errors on the view. The complex objects are not passed through the URL like the other values are. Is there a way to prevent this from happening? An alternative solution would be to remove all query parameters from the URL on a page refresh and display the view like it was a new object.
Controller
public IActionResult Daily(Daily daily)
{
new ReportDaily().GetAvailableSavedCriteria(out List<ReportCriteria> reportCriteria, out Notification not);
if (daily.SelectedCriteria == null) {
//Create daily report object and initialize the default values
var newModel = new Daily
{
PaymentTypes = DGetPaymentTypes(),
Users = DGetUsers(),
Criteria = reportCriteria,
StartDate = DateTime.Today.Date,
EndDate = DateTime.Today.Date,
County = true,
Municipality = true
};
return View(newModel);
}
else
{
daily.PaymentTypes = TempData.Get<List<Daily.PaymentType>>("PaymentTypes") == null ? DGetPaymentTypes() : TempData.Get<List<Daily.PaymentType>>("PaymentTypes");
daily.Users = TempData.Get<List<Daily.User>>("Users") == null ? DGetUsers() : TempData.Get<List<Daily.User>>("Users");
daily.Criteria = reportCriteria;
return View("Daily", daily);
}
}
TempData only used for a single redirect, to keep the data from another action after refreshing, you can use Session to achieve it.
To use Session in core mvc, you need to add following codes to the starup.cs fileļ¼
Add services.AddSession(); in ConfigureServices method.
Add app.UseSession(); in Configure method.
To save complex object in Session,you can convert the list object into a json format for storage, and then deserialize it into a list object when you get it.
HttpContext.Session.SetString("PaymentTypes", JsonConvert.SerializeObject(pamentTypeList));
Daily Action:
//.....
daily.PaymentTypes = HttpContext.Session.GetString("PaymentTypes") == null ? DGetPaymentTypes() : JsonConvert.DeserializeObject<List<Daily.PaymentType>> (HttpContext.Session.GetString("PaymentTypes"));
QUESTION
How do I safely and efficiently check if a view model has been populated with data, within the view itself?
EXPLANATION
I'm passing in a model to a view like so;
return View(response.Success ? (SalesDashboardViewModel)response.Model : new SalesDashboardViewModel());
Now, the view either has a fully populated view model with all the data, or, if the view model wasn't populated correctly, it may have an empty 'SalesDashboardViewModel'. If the second case is true, when I call #Model.CountOfUsers in the view for example, I will get a null object reference error.
How, in the view, would I go about checking if this is empty or not, other than checking if one of it's properties is null (due to the fact the properties may change).
FULL CODE BREAKDOWN
// Controller
public ActionResult SalesDashboard(){
var response = DashboardService.BuildSalesViewModel(User.Identity.GetUserId());
return View(response.Success ? response.Model : new SalesDashboardViewModel());
}
// Populating the SalesDashboardViewModel
public CustomResponseModel BuildSalesViewModel(string userId)
{
try
{
CustomResponseModel response;
var vm = new SalesDashboardViewModel();
response = GetCountOfSuspectsAddedThisMonth(userId);
vm.NoSuspectsAddedThisMonth = response.Success ? (int)response.Model : throw new Exception(response.Reason);
response = GetCountOfProspectsAddedThisMonth(userId);
vm.NoPropectsAddedThisMonth = response.Success ? (int)response.Model : throw new Exception(response.Reason);
response = GetCountOfCustomersNotContactedRecently(userId, 12);
vm.NoCustomersNotContactedRecently = response.Success ? (int)response.Model : throw new Exception(response.Reason);
response = GetTopProspects(userId, 10);
vm.TopProspects = response.Success ? (List<Prospect>)response.Model : throw new Exception(response.Reason);
return new CustomResponseModel { Success = true, Model = vm };
}
catch (Exception e)
{
return new CustomResponseModel
{
Success = false,
Reason = e.Message,
};
}
}
If there is a better way of going about this then I'm open to suggestions. I appreciate the help :)
You can use Safe Navigation Operator (?.) to access the properties, if you are not sure that they will have the value.
#Model?.CountOfUsers
Find more details here.
I am creating an CRUD Application in Asp.Net Core
After Add Operation I am redirecting to same view with setting model value as null to get another entry
Below is my code
public IActionResult Add(OptionMasterVM model)
{
try
{
model.QuestionList = context.QuestionMaster.Select(x => new SelectListItem { Text = x.QuestionName, Value = x.QuestionId.ToString() }).ToList();
if (HttpContext.Request.Method == "POST")
{
OptionMaster _optionmaster = new OptionMaster();
_optionmaster = model.OptionMaster;
using (var ctx = new QuestionnaireEntities(_configuration))
{
ctx.OptionMaster.Add(_optionmaster);
ctx.SaveChanges();
}
TempData["Msg"] = "Option Added Successfully , Add Another Option";
model.OptionMaster.OptionValue = string.Empty;
model.OptionMaster.OptionRating = 0;
return View(model);
}
}
catch (Exception ex)
{
logger.LogError(ex);
}
finally
{
}
return View(model);
}
Here I am setting Option Value to empty and rating to Zero to take next entry , but on view it does not show empty and zero , on view it show previously filled value.
After Setting below code these two fields should be reset but they don't
model.OptionMaster.OptionValue = string.Empty;
model.OptionMaster.OptionRating = 0;
Is there any other way to set model object as null in Asp.net Core ?
This can happen because Razor helpers use values from ModelState, rather than the model itself. Your OptionValue is probably displayed using a helper, for example:
#Html.TextBoxFor(m => m.OptionMaster.OptionValue)
When you change model values within an action, you need remove the old values from ModelState before rendering the View.
The easiest way of doing this is to call ModelState.Clear()
model.OptionMaster.OptionValue = string.Empty;
model.OptionMaster.OptionRating = 0;
ModelState.Clear(); // ensure these changes are rendered in the View
return View(model);
The values displayed for bound form fields come from ModelState, which is composed based on values from Request, ViewData/ViewBag, and finally Model. After posting, obviously, you'll have values set in Request, which will therefore be the values in ModelState. It works this way, so that when there's a validation error and the user is returned to the form to correct their mistakes, the values they posted will be there for them to edit.
Long and short, you need to follow the PRG (Post-Redirect-Get) pattern. Essentially, after posting, you only return the view on error. If the post is successful, you redirect. This not only clears ModelState, but also prevents accidental re-posts if the user attempts to refresh the page.
If you want to take the user back to the same view, simply redirect to the same action, but you need to do a redirect, not return the view.
Hi I am trying to develop a functionality that will track everytime there is a new session created in a web app aka a user logs in.
I have created a class called "StateBag.cs"
using System;
using System.Text;
[Serializable()]
public class StateBag
{
#region Business Methods
// To catch the event of a New Session //
private bool _NewSession = false;
public bool NewSession
{
get { return _NewSession; }
set { _NewSession = value; }
}
#endregion
}
On the login page, just before login:-
// Declaration Region. //
private StateBag _Bag;
if (Session.IsNewSession)
{
_Bag = new StateBag();
_Bag.NewSession = true;
// ViewState["StateBag"] = _Bag;
Session["NewSession"] = _Bag;
}
On the Main page, after a successful login:-
// Declaration region. //
StateBag _Bag
{
get
{
return (StateBag)Session["NewSession"];
}
}
if (_Bag.NewSession == true)
{
// Do my stuff........ //
_Bag.NewSession = false; // set new Session back to false//
}
I m having problems retrieving _Bag... it comes back as Null...
hence an error message :-
"Object reference not set to an instance of an object."
Can anyone help me retrieve the NewSession property which I set to "True" on the login page?
You're storing it in ViewState:
ViewState["StateBag"] = _Bag;
And retrieving it from Session:
return (StateBag)Session["NewSession"];
ViewState and Session are two completely different things, they don't share the same objects. You need to pick one place to persist the data and always retrieve it from that same place.
Note: ViewState renders data to the client, so I wouldn't suggest using that to store anything that you don't want a client to be able to see/modify.