Update Session during ajax request then access Session in the view - c#

Session["SelectedRecords"] is used to contain the Id's of selected records, e.g.
SelectedRecords: 1108392 1108393 1108394
When I fire an ajax post that removes an id from the Session["SelectedRecords"] which as far as server side is concerned successfully removes the ID from Session["SelectedRecords"]...
List<int> ids = SessionHelper.GetSelectedRecordIds();
ids.Remove(selectedItemModel.Id);
string csv = string.Join<int>(",", ids);
this.Session["SelectedRecords"] = csv;
using an onSuccess/onRequestEnd event I then use console.log to output the Session["SelectedRecords"] to check.
function onRequestEnd(e) {
console.log('onRequestEnd SelectedRecords: ' + #Session["SelectedRecords"]);
}
But the console.log shows that Session["SelectedRecords"] (as far as the client is concerned still contains the removed id?!)
So the question is when I remove a value from Session["SelectedRecords"] via an ajax post and afterwards access Session["SelectedRecords"] in the view, the view is reporting 'old' values i.e. not the latest values?!

Generally, when you have one object in Session and you POST a delete, you have to remove it also from the Session. This is done like below:
Session.Remove("key");
where key is the unique key you have used to store the item in the Session.
However, in your case this will not work. You have to do two things:
Remove the SelectedRecords from the Session.
Add the new SelectedRecords (without the one record you deleted with your POST) to the Session.
After doing this (which after your update it seems you already have this logic in your code), you have to return back to the client the new SelectedRecords and replace the old value with the new.
Something like this:
$("#selectedRecords").val(newSelectedRecords)
The above should be executed in the success callback of your AJAX call.

Related

Holding value between multiple pages

I'm working on rewriting an MVC application that currently utilizes cookies to hold data between multiple pages such as a date. I have tried to come up with an option that would hold the data in the controller by using something like this:
public DateTime HoldDate {get; set;}
The problem that I'm facing is that this is overwritten on each page load. I have also considered using Vue to store the variable and then sending the date to the controller on page load, but I'm not sure how to perform this.
Any help is appreciated and thank you in advance!
You can use TempDataDictionary to pass data from the controller to the view and back. It's a Dictionary, so you can access it with a key:
TempData["HoldDate"] = new DateTime(2020, 2, 13);
When you read data normally from TempData, it is automatically marked for deletion with the next HTTP request. To avoid that, since you want to pass this data around, use the .Peek() method, which lets you read the data but does not mark it for deletion.
var date = TempData.Peek("HoldDate");
If you need data to persist during the entire user session, you can use Session. For example user id or role id.
if(Session["HoldDate"] != null)
{
var holdDate= Session["HoldDate"] as DateTime;
}
If you need data only persist a single request - TempData. Good examples are validation messages, error messages, etc.
if (TempData.ContainsKey("HoldDate"))
{
var holdDate = TempData["HoldDate"] as DateTime;
}
TempData and Session, both required typecasting for getting data and check for null values to avoid run time exception.

What the most efficient way of monitoring changes to data on an ASP.Net form?

I have an ASP.Net form and I want to send an email when the user changes their data. The email should only include data that has changed, and there are about 15 data fields total.
I don't want to use an ORM since I am updating a website that a 3rd party built for us, and all their data access calls go through a custom library of theirs.
The only ways to do this I can think of is
Make another database call to get old values and compare the form values one-by-one. If they're different, append to the email.
Store original data somewhere when it's first loaded (hidden field, session, etc), and once again compare the data one field at a time and append the differences to an email
Have someone on SO tell me there's an easier and/or simpler way that I haven't thought of
All the text boxes will have a TextChanged event, you can have them mark themselves as modified. ComboBox's will have a SelectedIndexChanged event, and so on.
Edit: All changed events can check their initial values (even on reverted changes) and either mark themselves as still modified or on a revert, as un-modified.
Here are some suggestions that may / may not be useful:
Trigger on the database table and the trigger compares the old (using the DELETED table) and updated (using the INSERTED table) and then sends an email. This may or may not be viable and I am not a big advocate of triggers.
Like you have already said you could make another database call, which would be my reccommended approach.
From what you've said I think that the only way forward is to create a duplicate dataset on the form to store the old data and run a comparison at the point where you want to produce the email.
You can use Dataset.Copy to copy structure and data.
However, now that I think about it there's always the Datset.GetChanges() method and the Dataset.AcceptChanges() along with DataSet.HasChanges()
Example code from this link:
if(dataSet.HasChanges(DataRowState.Modified |
DataRowState.Added)&& dataSet.HasErrors)
{
// Use GetChanges to extract subset.
changesDataSet = dataSet.GetChanges(
DataRowState.Modified|DataRowState.Added);
PrintValues(changesDataSet, "Subset values");
// Insert code to reconcile errors. In this case, reject changes.
foreach(DataTable changesTable in changesDataSet.Tables)
{
if (changesTable.HasErrors)
{
foreach(DataRow changesRow in changesTable.Rows)
{
//Console.WriteLine(changesRow["Item"]);
if((int)changesRow["Item",DataRowVersion.Current ]> 100)
{
changesRow.RejectChanges();
changesRow.ClearErrors();
}
}
}
}
// Add a column to the changesDataSet.
changesDataSet.Tables["Items"].Columns.Add(
new DataColumn("newColumn"));
PrintValues(changesDataSet, "Reconciled subset values");
// Merge changes back to first DataSet.
dataSet.Merge(changesDataSet, false,
System.Data.MissingSchemaAction.Add);
}
PrintValues(dataSet, "Merged Values");

Passing parameters to Controller ..but NOT on the URL

Is there a way to pass a parameter to a controller without putting it on the URL?
For example,
http://www.winepassionate.com/p/19/wine-chianti-docg-la-moto
has the value 19 on the URL. If you actually change that value to another, the page displays a different record even it the page name remains the same.
So I would like to NOT pass the ID on the URL but still be able to pass that to the Controller.
What's the recommended way to do so?
You can do a post and send it as a form parameter. I do not recommend this. Posts should be for requests that modify data. In this case you're most likely looking just to get that data. The fact that the id is in the URL is a good thing (see the Stack Overflow URLs for reference). If you really don't want the user to be able to modify it (I hope it's not because you think this makes it more secure, because it doesn't), you could do some simple encryption on it to make it more difficult to guess/produce a valid ID.
Using TempData, as some other suggest, is not a robust solution. It won't work for links on a page, just a GET after POST, and then only once since TempData is deleted after the next request.
Well, you have a couple of options:
Is this a form post? If so, then you can simply add a specific key value pair to your form when you submit it and then data will be passed along.
Is the URL unique to that resource? i.e. Does "Wine-chianti-docg-la-moto" exist as a unique representation of the number 19 in a database somewhere? If so, then you can simply do a lookup of that route component in your database to retrieve the value you need (or push that logic all the way down to the database).
Is that a value that is not expected to change a bunch? You can set that value in Session or in a cookie that would be persisted across pages and then pull it from the respective collection.
Are you redirecting to this page from another request on your server? If so, then you can use TempData to store this temporary value. However, I would recommend against this approach, as it is very transient and not good practice imo.
Lastly, you can obscure the value on the URL if you dont want it to be easily user editable. Encrypt it with some algorithm, and then decrypt it on the destination page. The user will be unlikely to be able to alter the ID by typing in a different value in the URL.
If the page is a GET, and you are following the PRG like you should be (Post-Redirect-Get) then you can use TempData["dataName"] = value; in your [HttpPost] controller and then consume it in your [HttpGet] method. It really depends on how the page is being called.
However, there is nothing wrong in letting the user change that number if it is not security related, and is common practice to show non-vital information in the url like that.
You should use TempData in this case. A good read on this can be found on this blog.
TempData allows you to store a value temporarily between requests and is, by default, erased after being accessed.
// TempData samplepublic ActionResult Featured(){ var featuredProduct = new Product { Name = "Assorted Cupcakes", Description = "Delectable vanilla and chocolate cupcakes", CreationDate = DateTime.Today, ExpirationDate = DateTime.Today.AddDays(7), ImageName = "cupcakes.jpg", Price = 5.99M, QtyOnHand = 12 };

Limit user to filling out form only once

I'm looking to create a simple web form, and I would like to "discourage" users from filling a form out multiple times. The metrics of the form are used for statistical analysis, and each time a user fills out and resubmits the form, the result set usually changes, and hence analysis.
While we don't want to BLOCK re-trys (knowing that a re-try was done is also valuable information), we do want to warn users: "Hey, it looks like you filled this out recently. Are you sure you want to fill it out again?"
The caveat here is that we want to minimize the amount of personably identifiable information collected.
Is storing a cookie with the clients IP the best/simpliest way to do this? Or is there a simple method for caching an IP server-side for xx amount of time so we can run a comparison to says "hey, I think this guy tried to access me earlier today. I should present a warning".
Cookie with constant value should be enough, not even IP. If user did not cleared cookies you'd know that the user already filled out the form.
On easy solution I've used before is to put an invisible timestamp in the HTML form the user fills out. If you get submitted the same timestamp twice, you know its a re-submittion.
If you're worried about tampering, you can always mix up/encrypt the timestamp.
This could also just be a random unique identifier, I chose a timestamp in order to know how long a user took filling out a form (roughly).
This is basically like a session cookie, but might be considered more "private" as theres nothing for a client's computer to remember so it can't be used as like some tracking cookies ad sites.
The downside is that this method requires that a client/proxy not cache the form as it "changes" every request.
There are two issues here the user clicking the submit button multiple times, and the user filling in the form at another point in time.
For the second I can see two quite simple solutions would recommend a session cookie, just cookie the users machine and don't let them see the form again, or ask for a piece of information like email address and then check in the DB if its been used before, if so disregard the new form details.
For the multiple form submit you can use this code, which will disable the button onclick.
//
// Disable button with no secondary JavaScript function call.
//
public static void DisableButtonOnClick(Button ButtonControl)
{
DisableButtonOnClick(ButtonControl, string.Empty);
}
// Disable button with a JavaScript function call.
//
public static void DisableButtonOnClick(Button ButtonControl, string ClientFunction)
{
var sb = new StringBuilder(128);
// If the page has ASP.NET validators on it, this code ensures the
// page validates before continuing.
sb.Append("if ( typeof( Page_ClientValidate ) == 'function' ) { ");
sb.Append("if ( ! Page_ClientValidate() ) { return false; } } ");
// Disable this button.
sb.Append("this.disabled = true;");
// If a secondary JavaScript function has been provided, and if it can be found,
// call it. Note the name of the JavaScript function to call should be passed without
// parens.
if (!String.IsNullOrEmpty(ClientFunction))
{
sb.AppendFormat("if ( typeof( {0} ) == 'function' ) {{ {0}() }};", ClientFunction);
}
// GetPostBackEventReference() obtains a reference to a client-side script function
// that causes the server to post back to the page (ie this causes the server-side part
// of the "click" to be performed).
sb.Append(ButtonControl.Page.GetPostBackEventReference(ButtonControl) + ";");
// Add the JavaScript created a code to be executed when the button is clicked.
ButtonControl.Attributes.Add("onclick", sb.ToString());
}

Getting more than one item from a database in ASP .NET MVC 3

I'm creating a database where users can enter some Error Reports and we can view them. I'm making these database with C# in the ASP MVC 3 .NET framework (as the tags imply). Each Error Report has a unique ID, dubbed ReportId, thus none of them are stored under the same Id. However, whenever a User creates a new Error, I pass their User Name and store it in with the rest of the report (I use User.Identity.Name.ToString() to get their name and store it as a string). I know how to get a single item from the data using a lambda expression, like so:
db.DBSetName.Single(g => g.Name == genre)
The above code is based on an MVC 3 tutorial (The Movie Store one) provided by ASP. This was how they taught me how to do it.
My major question is: is there a member function like the .Single one that will parse through the whole database and only output database entries whose stored User Name matches that of the currently logged in user's? Then, I can use this to restrict User's to being only able to edit their own entries, since only their entries would be passed to the User's View.
What would be the best way to implement this? Since the ReportId will not be changed, a new data structure can be created to store the user's Errors and passed through to the Index (or Home) View of that particular controller. From there they should be able to click any edit link, which will pass the stored ReportId back to the Edit Action of this particular controller, which can then search the entire database for it. Am I right in assuming this would work? And would this be ideal, given that the other items in the database are NOT passed through to the Index in this method, meaning the User does not have access to the other items' ReportId's, which the user needs to pass into the Edit Action for it to work? If this is ideal, this is the method that requires me to know how to parse through a database and grab every element that fits a particular description (stored User Name matches User's current User Name).
Or would a better approach be to pass the whole database to the Index View and only output the database entries that have User Name values that match the current logged in user's? I guess this could be done in a foreach loop with a nested if loop, like so:
#foreach(var item in db.Reports)
{
if(item.UserName == User.Identity.Name.ToString())
{
...code to output table...
}
}
But this passes the whole database which gives the user a lot more info than they need. It also gives them potential access to info I don't want them to have. However, I don't have to make a new data structure or database, which should lower server memory usage and fetch time, right? Or are databases passed by copy? If so, this method seems kinda dumb. However, I don't know if the first method would fracture the database potentially, this one certainly would not. Also don't remember if I NEED an else statement in C#, I'm more familiar with C++, where you don't need one and you also don't need {}'s for single line if's, if I need one: please don't judge me too harshly on it!
Small note: I am using CRUD Controllers made with the Entity First Framework in order to edit my database. As such, all creation, reading, updating, and deletion code has been provided for me. I have chosen not to add such basic, common code. If it is needed, I can add it. I will add what the Edit Action looks like:
public ActionResult Edit(string id)
{
Report report = db.Reports.Find(id);
return View(report);
}
It accepts a string as an id, ReportId is the id used and it IS a string. It is a randomly generated GUID string made with the GUID.NewGuid().ToString() function. I will also be doing the comparison of names with:
Model.UserName == User.Identity.Name.ToString()
Which was shown earlier. Sorry if this is too much text, I wanted to provide as much info as possible and not make anyone mad. If more info is needed, it can certainly be provided. So at the end of the post, the major question actually comes down to: which of the above two methods is best? And, if it's the first one, how do I implement something like that?
Thanks for your help!
Unless I'm completely misunderstanding you, you just want .Where()
Like this:
var reports = db.Reports.Where(r => r.genre == inputGenre);
This would get you an IEnumerable of Report, which you could then use however you wish.

Categories