hit insert several times and got duplicates - asp.net

I have a simple asp.net web forms page that does an insert to my sql server db. My server was running slow at the time and I pressed Insert button several times because I didn't think it took but it did all 3 times.
So I have duplicates from that one interaction. How would I prevent this?
Thanks,
rod.

Bind the onClick of your button to a javascript and disable or hide the button and optionally display a message like "Please Wait.." to avoid multiple clicks
Code Behind:
Button.Attributes.Add("onclick", "fn_OkClick();");
ASPX file:
<script language="javascript" type="text/javascript">
function fn_OkClick()
{
var okieButton = document.getElementById('<%=Button.ClientID%>');
okieButton.style.display = 'none';
}
</script>

I'd first evaluate if the business rule is correct. Are you sure you shouldn't be able to insert multiple records with those same details? Generally I would think there would be some validation around whether or not something can be inserted. If you can do a blind insert then it suggests you should be able to do multiple inserts of the same data.
Take a resource booking program. For a given resource, at a given time, it can only be booked once. Therefore you would want to prevent people doing what you've done and inserting multiple records into the booking table for the same item at the same time.
As such there should be some validation before you try to insert the record to ensure that the selected resource is in fact available at the time you've selected. If so, insert the record, if not, don't insert the record, and return a meaningful reason to the user as to why.
If there is no validation, then there should be no reason to prevent the user from inserting multiple records.

Related

Application Express: Anonymous PL/SQL Block and Bind Variables

I'm having an issue binding the value of a page item to a declared variable in an anonymous PL/SQL block process.
The problem is that the page item (:P4550_REQUESTOR) is not populated with a value until a conditional is met. It appears that the PL/SQL block process is binding the variable to an empty value as soon as the page is loaded, despite the fact that the process does not fire until a specific button has been clicked.
Here is my code:
DECLARE
v_email_to app_user.email%type;
v_requestor VARCHAR2(15);
BEGIN
v_requestor := :P4550_REQUESTOR;
BEGIN
SELECT email INTO v_email_to
FROM app_user
WHERE userid = v_requestor;
END;
SEND_APEX_MAIL (
v_email_to,
'Your vacancy request has been rejected.'
|| chr(10)
|| 'Emailed to: ' || v_email_to
|| chr(10)
|| 'Requestor: ' || v_requestor,
'Vacancy Request Rejected'
);
END;
Does anyone have any thoughts on this?
The block works just fine if I hard code a value to v_requestor. If I try to get the value of P4550_REQUESTOR after the page has loaded, it is empty. After clicking the edit button, P4550_REQUESTOR is populated.
** **MORE DETAIL** **
P4550_REQUESTOR is a page item that resides within the Vacancy Request region which is only displayed when a conditional is met. Specifically, the conditional is an edit button associated with a table row that is created on page load. Clicking the edit button causes the details region to display, and the associated page items to be populated.
The page item values in the Vacancy Request region are populated via an Automated Row Fetch which fires After Header.
P4550_REQUESTOR has a Source Type of DB Column.
The process that fires the code above is set to fire On Submit - After Computations and Validations
If I log the value of P4550_REQUESTOR when the page loads, it shows null. If I log the value after clicking the edit button, I get the expected string value.
Process Flow Control in Oracle APEX
(This is actually useful to think about in other programming disciplines and environments.)
Problem Defined
The problem is that the page item (:P4550_REQUESTOR) is not populated with a value until a conditional is met. It appears that the PL/SQL block process is binding the variable to an empty value as soon as the page is loaded, despite the fact that the process does not fire until a specific button has been clicked.
The problem statement reworded in Apex terminology and presented in the form of an actual question:
There is a REPORT REGION on the page which contains the result of a direct reference to a data table/view. This report is managed by an Apex process called "Automated Fetch" and is initiated automatically by the loading of the page headers.
There is a FORM ITEM on a page which which is populated conditionally by a BUTTON ITEM selection made by the user. The BUTTON ITEM is part of the report results.
There are multiple button items. Each is associated with a value for each report record.
If the user does not select the BUTTON ITEM from the REPORT REGION, the FORM ITEM remains unassigned and contains a "null" value.
There is a defined PL/SQL block of code which is set to execute when a SUBMIT BUTTON item is pressed (also on the same page). Why does my code block (defined page process) run with a null value when it is triggered without first pressing a BUTTON ITEM from the REPORT REGION first?
Event Driven Program Design for Procedural Programmers
The answer is not obvious if you think under the paradigm of a procedural language. Without diving into a lecture on the topic, here's a visual layout of the problem space of the OP that I cooked up to illustrate how the problem can be made more obvious:
This is my Apex page design in implementation. It's generic enough to use as a template for other Apex designs. There are no flow arrows on this diagram because it's a stateful system. One thing causes another thing to happen and so on... but not always and not all at the same time.
Use Cases for Apex UI Page Designs
Try walking through a few use cases to understand how the elements broken down in the diagram operate together. Each user may take any number of click combinations and interactions, but there is a commonality:
They all enter the same initialized conditions on page load.
They all leave the page by: navigating elsewhere or through the SUBMIT button event.
Use Case #1
User chooses {MyPage:SQLReport:ThisButton} from one of the records in {MyPage:SQLReport}
According to {MyPage:SQLReport:ThisButton} #3, the value associated between the report record and the button item is passed to: {MyPage:HTML-Region:ThisItem}
The form item state has been updated and changed from the initial null value.
User selects {MyPage:HTML-Region:ThisSubmit} button to inform the system to continue on.
The submit button executes the defined PL/SQL procedure block: {MyPage:RunCodeBlock}
Use Case #2
User enters page and reviews results displayed in the {MyPage:SQLReport} region.
User decides no additional input is necessary and then selects the {MyPage:HTML-Region:ThisSubmit} button to inform the system to continue on.
(a note: the state of form item {MyPage:HTML-Region:ThisItem} has not been changed from the initial null value at this point... after the submit button has been selected)
The submit button executes the defined PL/SQL procedure block: {MyPage:RunCodeBlock}
Use Case #3
User chooses {MyPage:SQLReport:ThisButton} from one of the records in {MyPage:SQLReport}
According to {MyPage:SQLReport:ThisButton} #3, the value associated between the report record and the button item is passed to: {MyPage:HTML-Region:ThisItem}
The form item state has been updated and changed from the initial null value.
User chooses {MyPage:SQLReport:ThisButton} from a different selection from one of the records in {MyPage:SQLReport}.
According to {MyPage:SQLReport:ThisButton} #3, the value associated between the report record and the button item is passed to: {MyPage:HTML-Region:ThisItem}
The form item state has been updated and changed from the initial value stored in step (2).
User selects {MyPage:HTML-Region:ThisSubmit} button to inform the system to continue on.
The submit button executes the defined PL/SQL procedure block: {MyPage:RunCodeBlock}
The difference between each case should illustrate why the dependent value (ThisItem, or more specifically, page item P4550_REQUESTOR) is null in one use case vs. the other.
Building a Physical Implementation (An Apex Page)
The table I used is called STAR_EMPS. It is similar to the EMP table but has only three columns: ename, deptno and salary. Although it is not super important, this is the data set I used to populate STAR_EMPS:
I used a simple two-column table named STAR_EMPS_LOG for capturing the output of a successfully executed procedure call. You could accomplish the same with just one column, but I wanted a sequential id for tracking the order each event was recorded- for running multiple test cases. The procedure is one of several defined processes kept on this page:
contained in: {MyPage:RunCodeBlock} is below:
DECLARE
-- output from this procedure will be recorded in the star_emps_log
-- table. {MyPage:RunCodeBlock}
mycelebrity star_emps.ename%TYPE:= :P17_CELEBRITY_NAME;
mylogmessage star_emps_log.log_message%TYPE;
BEGIN
-- Conditional; changes message based on the value set for the
-- page item.
if mycelebrity is null then
mylogmessage:= 'No button was pressed on the previous page.';
else
mylogmessage:= 'The user selected: ' || mycelebrity ||
' from the report list.';
end if;
-- populate value from the page item.
INSERT INTO star_emps_log (log_message)
VALUES (mylogmessage);
commit;
END;
This is how the page layout was set up:
As in your example, I made a {MyPage:SQLReport} region with its supporting elements. The SQL Report represents a query directed at the source data table.
{MyPage:Form} has been renamed to {MyPage:HTML-Region}.
{MyPage:SQLReport} is defined by a SQL query, there is also a mock column to use as a place holder for placement of the "edit" buttons.
{MyPage:SQLReport:ThisButton} The button specifications are detailed through this:
The TWO Page processes: PROCESS and BRANCH need to be linked with the same settings referencing a BUTTON triggering Item.
User Interface Test Cases
Run through the three suggested scenarios to get started. Verify that the system is interpreting the requests correctly. This is what the page layout looks like:
The two processes on the system have a definition that wasn't mentioned in previous discussions may solve our original problem at hand:
Some Parting Thoughts
It is a good thing this turns out to be a trivial case once broken down. The diagramming method described here should scale to other Apex applications of varying complexity. There is considerable utility in stepping away from the code, locking down on terminology and trying to describe systems and processes without actual code. Please be sure to share any stories if this approach helps with your own Oracle Apex design challenges.
Onward!
The original, verbose answer seems to way overcomplicate the issue. The session state concepts manual covers this behaviour more succinctly.
Should P4550_REQUESTOR be a normal item created from a wizard, using :P4550_REQUESTOR will return a value in processes running post submit because the submit processes moves values in browser to session state.
If P4550_REQUESTOR is rendered conditionally, then it will always be null and I'm not sure what would happen if you tried to set it - probably depends how.
On a similar note, if you used &P4550_REQUESTOR. to parameterise the process, you would face the behaviour originally described (and made the code less secure)

Do I need to re-retrieve data when paging using asp:GridView

I have a .aspx search screen that displays the results of the search in an asp:GridView component. There can be up to approx 1000 records returned by the search. I want to implememt paging on the grid so that only 15 records are displayed at any one time and the user can page through the results.
I am retrieving the records by passing search parameters to a WCF service which returns a List of particular entity objects. I create a Datatable and insert one DataRow per entity object from the List. I then bind the grid view to the Datatable.
This is how my grid is defined in the .aspx page:
<asp:GridView ID="gridCat" runat="server" AutoGenerateColumns="False" DataKeyNames="CatalogueID"
HeaderStyle-CssClass="fieldHeading" RowStyle-CssClass="fieldContent"
AlternatingRowStyle-CssClass="alternateFieldContent" Width="100%"
AllowPaging="True" AllowSorting="True" AutoGenerateDeleteButton="True"
PageSize="15">
and I also have this method in the code behind (.aspx.vb file):
Sub GridPagingAction(ByVal sender As Object, ByVal e As GridViewPageEventArgs) Handles gridCat.PageIndexChanging
gridCat.PageIndex = e.NewPageIndex
gridCat.DataBind()
gridCat.Visible = True
End Sub
My problem is this: the first page is rendered correctly i.e. the first 15 records are displayed correctly. However, when I navigate to page 2 in the grid, the GridPagingAction method is hit on the server but nothing is displayed in the grid - it is just blank.
I think the reason this is happening is because the Datatable no longer exists on the server when the request for the second page hits the server - is that right? And the asp:GridView, when it is rendering the first page of results, only takes the first 15 records from the Datatble and sends them back to the browser. So when the request for the second page comes in the other records (i.e. records 16 - 1000) don't exist anywhere - is all that correct?
If so, what's the best solution - I can't see how to implement paging without having to do one of the following:
re-perform the search each time the user uses the paging option;
saving the search results on the Session after the first Search retrieving them each time the user uses the paging option;
manually inserting the Search results into ViewState and retrieving them each time the user uses the paging option.
Is there a better way to do this (or am I doing it wrong)? If not, which of the 3 options do you think is the best? I'm leaning towards option 2 as I don't think option 1 is performant and I don't want to be sending loads of unnecessary data back to the browser as per option 3.
All you said is correct. You could either use ViewState or the Session to keep hold of the data on client- or server-side, but if you really have that many records, it might be a good idea to only collect the data you actually need.
So if you want to show records 1 to 10, you perform a query against the database and only fetch those ten records. If you want to show the next ten, you perform another query with the according parameters.
This will improve your performance and memory usage significantly, IF calling your DB is not overly expensive.
This article might give you a start on how to do this:
http://dotnetslackers.com/articles/gridview/Optimized-Paging-and-Sorting-in-ASP-NET-GridView.aspx
If you want an easy solution without any additional efforts, I would query all the records on each postback (your option #1).
If you want the best performing solution with not much overhead, use the custom paging.

ASP: Data binding with controls

I am implementing an online parking reservation system and I need to bind a table with 2 Controls.
for example the user selects a Reservation start date and the parking location from a RadioButtonList and then a button (Search Availability) is pushed to fetch the parking from the database according to the Date selected and Location.
the question is: How can I bind the (Reservation Start Date Control) with (RadioButtonList) to both search in the database? and what would be the Sql Query?
Regards.
This is pretty basic stuff so you've got a lot of work ahead of you.
On your aspx page, you will want to use a SqlDataSource and add two ControlParameters to the SelectParameters, one for the RadioButtonList, one for the TextBox/Calendar with the date. Then create a GridView control to display the results and set the DataSource of the gridview to be the SqlDataSource.
Depending on your database schema, the SQL Statement will look something like this:
SELECT * FROM [Parking] WHERE [LotID] = #LotID AND [Date] = #Date AND [Reserved] = FALSE;
However, I have done reservation systems in the past, and queries to find available spots for a particular day are rarely simple. I would suggest worrying about writing the SQL query first and then getting the web page to run the query later. If you post information about your table schema and tag it as a SQL question you'll probably have better luck.
Hope this helps.

Processing variable number of form fields

I am working on a form which displays information about orders. Each order has a unique id, but they are not necessarily sequential on the form. Also, the number of fields can vary (one field per row on the form). The input into the form will not be mapped straight into the database, but will be added to the current value in the database, and then saved. An example of the form is in the picture below - the callout on the right shows the id for each row.
I know how to generate the form like this, but I can't work out how I can easily process each of these rows reliably. I also know how to give each of the fields a unique identifier, like name="order-23" or name="order[23]", but how can I translate that name so that I can update the related record in the database?
EDIT: One solution I can think of would be to iterate through every form field in the FormCollection, and if the name of the field matches the pattern, then I will extract the number from that field-name and process it.
However, I feel that there must be a much easier way to go about it - this method would likely involve a fair bit of string processing on each field, and there would possibly fall over if I have to add extra fields for each row later on.
Don't you have a list of IDs after postback? I believe you should not depend on what IDs are actually sent from the form, as anybody could change the IDs on the form to whatever they want, so it's a security issue. So you should after postback have a list of IDs you want to update (the same list you used to create the form with). In that case, you know exactly what id string you should use to retrieve the value from FormCollection.
In case you really can't get the list of IDs you are going to update, just use the FormCollection iteration as you suggested in your comment. The string processing is not that expensive in comparation with all other stuff being done at request processing.
If you have the names, then simply read the values by using Request.Form["order-23"] or re-create the controls in page pre-init and you'll have access to the values in your save event directly through the created controls.
I've done this loads in my CMS.
Essentially i sort of cheated.
The idea is something like this ....
render the form to the client, then look at the source code gneerated.
You should see that it generated a form tag with an action attribute.
When you click the submit button the form will be sent to that url so in the case of an order submission you would post the page back to OrderPage.aspx?OrderId=xxxx
then on the server you would build an update statement for your db that contained something like ...
"Update orders where order id =" + request.querystring["OrderId"]
So how do you update the action ...
In the calling page lets say you have a link called "New Order", when that link is clicked you need to do 2 things ...
redirect to this page.
generate an order id for this new order.
ok the first is simple, simply link it to this page.
the second ...
if this page is not a postback if(!IsPostback) { /* get a new id */ } depending on your order id's this may involve generating a new guid or doing something like getting the next number in a list by doing a select max(id) from a db.
once you have this new id you should still be in the page_load event.
this.form.Action = this.form.Action + "?OrderId=" + yourNewOrderId;
... tada ...
Send page back to the client.
view the source.

What is the best approach for this CRUD search page in ASP.NET

I'm building a heavily CRUD based ASP.NET website and I've got to the phase of the project where I'm trying to build a search webpage that shows the user different subsets of a certain important table based on parameters they enter such as dates and different text fields.
I can't use a fixed SQL statement, because depending on whether they search by date or by something else, the SQL would be different, so instead I have been generating a results list using a table web control that starts out invisible and then is filled and set to visible once the user identifies a search they want to make. I used a table control because its easy to create and modify in the code behind, and I was able to make the needed calls to the database and populate that table as required.
However, the second thing I need with this search page, is the ability to allow the user to select a record from the results and then go edit it using the rest of the CRUD based pages on the site. To do that, I created asp:buttons in the table and gave them all different ids and the same event handler. But since I created the buttons dynamically, it seems the event disappears on callback and my scheme fails, so I'm taking a second look at how to do this.
My current code for the events looks like:
tempcell = new TableCell();
Button tempbutton = new Button();
tempbutton.Text = "Go";
tempbutton.ID = "gobutton" + rowN;
tempbutton.Visible = true;
tempbutton.Click += new EventHandler(tempbutton_Click);
tempcell.Controls.Add(tempbutton);
temprow.Cells.Add(tempcell);
My results table is declared like this:
<asp:Table ID="ResultTable" visible="false" runat="server"
GridLines="Both" CellSpacing="8">
</asp:Table>
I'd like to be able to identify which record the user selected, so I can send that info on to another page. And of course, I still need to be able to generate results for several different search criteria. Currently I have date, template and code based searches that lead to different stored procedures and different parameters based on what the user has entered.
Any suggestions on this? It seems like the data grids and repeaters require SQL statements in advance to work properly, so I'm not sure how to use them, but if I could then the item command approach would work with the buttons.
Your event will hook up successfully if the button is recreated in the page load event.
Otherwise, use a GridView. It is the perfect solution for simple CRUD, and you can control it as much as you need.

Resources