Asp.Net GridView EditIndex race condition? - asp.net

This is a bit of a hypothetical question that has sent me off down the garden path... Say I have a gridview that I'd like to edit... given a method that binds the data..
private void BindGridFirst() {
var data = new List<string>() {
"A","B","C","D","E","F"
};
gridView.DataSource = data;
gridView.DataBind();
}
Now presume that I'm looking at this page, and another user has come along and made some changes to the underlying data, and I now go and click the edit button to edit D...
The edit method is pretty straight forward:
protected void RowEdit(object sender, GridViewEditEventArgs e) {
gridView.EditIndex = e.NewEditIndex;
BindGridSecond();
}
Edit: I feel compelled to point out, that this method is used in pretty much all the online examples, including ones from Microsoft.
This BindGridSecond() method looks like so:
private void BindGridSecond() {
var data = new List<string>() {
"A", "AA", "B","C","D","E","F"
};
gridView.DataSource = data;
gridView.DataBind();
}
It's exactly the same, but the data is now changed. Once the UI updates, the user is now in edit mode against row C.
Not what the user expected or wanted. How should this scenario be handled to avoid such an issue?

Personally, I use the DataKeyNames property and the SelectedDataKey on the GridView, so that I can easily obtain the primary key of the row the user wants, rather than relying on the index of the grid.
By using the primary key, you don't have any issues with new items being added to the collection, such as in your example. Plus, using the primary key makes it easier to deal with paging on the grid, as you don't have to take the page number and index into account.

Imho there are two options:
You could cache the Data you want to bind to the Grid in a Session for example. So you are able to check for changes before you call the BindGridSecond-Method and alert the user if any changes have been made while he was browsing the Page.
In option 2 you would again cache the Data you were binding in the BindGridFirst-Method and just work with this data for the next PostBack actions. So you don't have to worry about changes that may occur while browsing the Grid.

Related

Linq data not updating form after postback

In my application, I have a form that users fill out, then gets approved by a manager. I have various types of forms that all use the same process, so the approval buttons are all done via a user control (which includes the functionality to update the data in the database and call the postback).
However, once I click on the "Approve" button (which is in the user control), the form information doesn't update (it still says "unapproved"). A postback is definitely happening, but not sure why the page isn't updating properly.
I can confirm that the change are being made - when I manually reload the page, it gets updated - but not on the post back.
What am I missing here?
My page:
protected void Page_Load(object sender, EventArgs e)
{
int ID;
// ensure that there's an ID set in the query string
if (Int32.TryParse(Request.QueryString["ID"], out ID))
PopulatePage(ID);
else
Response.Redirect("~/Default.aspx");
}
}
protected void PopulatePage(int ID)
{
using (WOLinqClassesDataContext db = new WOLinqClassesDataContext())
{
lblStatus.Text = wo.Workorder.status;
....
}
}
I think that the Page_Load happens before the code in the submit button. To check this just use a couple of breakpoints. So the page loads the old data since the new data are not saved yet.
You should call a method to load the data inside the OnClick method of the Approve button.
After you've submitted the changes to the database, try running db.Refresh(RefreshMode.OverwriteCurrentValues) to force the changes to be reloaded into the data context.

Edit Update Delete in membership

I am creating page for admin to view all user in the system database. I’m using gird view to retrieve all the users in membership table. The problem now is how can Admin edit, delete and update the changes made by the admin? When we want to configure the select statement, there's advance button which we can put some additional statements. The membership table in my SQL doesn’t have a primary key. How do I solve this? Much thanks.
Have a look at this tutorial, it does exactly what you ask http://www.codeproject.com/Articles/24085/Insert-Update-Delete-with-Gridview-simple-way
That tutorial that Ashwin suggested looks way too involved for me. The direction I would take...
Store the username field in the datakey property of the gridview. And use the RowDeleting and RowUpdating events of the grid view...
protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
e.Cancel = true; // cancel default action.
// delete user myself.
string user = e.Keys["username"].ToString(); // think that's the name of the field in database.
Membership.DeleteUser(user);
}
protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
e.Cancel = true; // cancel default action.
// update user myself.
var userToUpdate = new MembershipUser();
// get new values with e.NewValues[] and fill out all properties of userToUpdate.
Membership.UpdateUser(userToUpdate);
}
Calling methods from the membership object seems much easier in my opinion, and then you don't have to mess with the tables that asp.net generates which could mess something up if done incorrectly.

Customizing output of datasource in repeater?

I have a Data Repeater hooked up to a datasource (datatable object). I need to change the output on the frontend for certain columns under certain conditions. What would be the most efficient way to do this?
I am currently trying to create the formatted output and assign it to another datatable and use that as the data source, but it seems overly complicated and something that would be hard to maintain.
Is there an easier way to manipulate column values for a datasource? I need the ability to check the previous and next rows for the source as that is a basis for some of the column values.
If you're talking about simple manipulation, the DataBinder.Eval method accepts a format string:
<%#Eval("SomeMoneyColumn", "{0:C}")%>
If the format string is not sufficient, you could create a method in the code-behind to handle the formatting, like this:
<%#FormatData(Eval("SomeColumn"))%>
In code-behind:
protected string FormatData(object data)
{
return String.Format("My name is {0}", data);
}
You can also use the ItemDataBound event too. Using this technique, you can still access the datasource object, in the case that your manipulation involves other data that is bound to the repeater.
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
Label lblCtrl = e.Item.FindControl("SomeControl") as Label;
if (lblCtrl != null)
{
lblCtrl.Text = String.Format("My name is {0}", DataBinder.Eval(e.Item.DataItem, "SomeColumn"));
}
}
I don't think there's a way to do what you want on the client side easily w/o using special logic like you are doing now. If you are getting data from a database, you could potentially do all the data manipulation on the DB side and pass it along transparently to the front end.

How to stop an Loading function on button click?

I have a problem, I have to stop one Loading class on button click. I already checked some forums related to this. But didn't find an exact solution.
For example:
Public Sub LoadDropDown()
Dim it As Integer
For it = 0 To 1000000
DropDownList1.Items.Add(it)
Next
End Sub
I have to load the DropDown on Load button click and I have to cancel that on cancel button click.
Since populating the control happens on the server, I can't imagine way to interrupt your method from the client. The whole control is being populated, rendered, and only then sent to the client. You might interrupt the callback using ajax, but then the control wouldn't be returned at all.
An alternative could be to load the contents in chunks with ajax and append them to the control on the client-side.
There is no formal way to do what you're asking, but you should be able to achieve the same result if you refactor your code. If certain content shouldn't be loaded for certain users, do that logic in your code behind before it renders to the page.
Per your response to the other answers...
You could consider doing multiple my_ddl.items.add() calls on a timer. Would involve multiple, separate postbacks / ajax calls. For example:
1) add records for 2 seconds (instead of a fixed number of records at a time)
2) check for session("continue") = "true"
3) add more records for 2 more seconds
4) check session("continue")
...
At some point, user clicks cancel, which assigns "false" to session("continue"). Next time your loop checks session("continue"), it will see that it's false and exit.
This would give you a partially-loaded data control. You might want other code to wipe-out the partial update.
I think you could accomplish this with a Session Variable. Forgive me, but I'll have to provide the example in C#, but I'm sure you can get the general idea of this.
private bool CancelRequested
{
get
{
if (Session["CancelRequested"] == null)
return false;
else
return (bool)Session["CancelRequested"];
}
set
{
Session["CancelRequested"] = value;
}
}
public void LoadDropDown()
{
for (int it = 0; it <= 1000000; it++)
{
if (CancelRequested)
{
CancelRequested = false;
break;
}
//Your logic here
}
}
protected void btnCancelRequest_Click(object sender, EventArgs e)
{
CancelRequested = true;
}
The idea here is that the inital loop checks a Session variable to see if it should continue or break out of the loop. If you have a button on the page that will allow the user to set this Session variable to "true", they can essential communicate to the inital request and cause it to break out of the loop. I'm not sure if this would fully accomplish what you're looking to achieve, but hopefully it helps.

Why is a child property getting overwritten when a Linq object is being updated?

I'm updating one object, and trying to update any child objects along with it.
Basically I'm handling the OnUpdating event of a LinqDataSource.
In the DataContext class I have the ObjectUpdate function (where right now I've just got a breakpoint so I can see the values...)
In the LinqDataSource.OnUpdating event e.NewObject.Child is null, which makes no sense whatsoever. I set that to a new value, but by the time I get to DataContext.ObjectUpdate NewObject.Child has been overwritten with the OLD value...
So somewhere between LinqDataSource.Updating and DataContext.UpdateObject it's populating the object with the old values... but I need the new ones.
Is there a way to fix that, or am I going to have a nervous breakdown?
I think I figured out the problem. After running LinqDataSource through .NET Reflector I noticed that:
1) It's the LinkDataSourceUpdateEventArguments.OriginalObject which is actually attached to the data context
2) values are copied from the NewObject into OriginalObject after OriginalObject is attached to the data context
What I don't understand is why the association properties are not copied. Maybe for the same reasons you can't serialize them?
The workaround is/was to handle the Updating event myself and do the actual submit instead of letting LinqDataSource handle that part.
void FormDataSource_Updating(object sender, LinqDataSourceUpdateEventArgs e)
{
var newObj = e.NewObject;
var table = FormContext.GetTable(e.NewObject.GetType());
if (BuildingObject != null)
BuildingObject(sender, new HeirarchicalBuildObjectEventArgs(newObj));
table.Attach(newObj, e.OriginalObject);
FormContext.SubmitChanges();
e.Cancel = true;
}

Resources