Handling Non-Updateable Database Views in Iron Speed Designer V3.X
Views provide us with a quick and efficient way to generate an information rich and user friendly display of data.
- Michael Weiss, Independent Developer

December 28, 2005
Iron Speed Designer V3.2

Database Views and Iron Speed Designer
Database views in Microsoft SQL Server 2000 provide a flexible and efficient way to display data. Using a view allows us to pull data from many tables, incorporate sophisticated Transact-SQL and user defined functions, grab the Top N or Bottom N records, and even pull records from partitioned tables and tables located on disparate servers.

Iron Speed Designer provides developers with some great properties to facilitate working with views. Virtual primary keys and virtual foreign keys, the ability to select the column you want displayed in place of a foreign key value...these all greatly enable the use of views in an Iron Speed Designer application.

Recently I found myself needing to display users’ expense data in an application. By using a view I could easily bring in other supporting data and give the user a richer and more meaningful data viewing experience than would be possible by pulling data from the Expenses table alone.

Creating Queries

For the purpose of this article, let’s assume we have the following tables and we create the query in Query Listing 1 over these tables to display data for our users in a Show Table Page in Iron Speed Designer:

  1. Table: Expenses – ExpenseKey pk, ClientKey, ExpenseTypeKey, CheckNum, PaymentMehodKey, ExpenseAmount, EmployeeKey, Notes.

  2. Table: Clients – ClientKey, FirstName, LastName, etc.

  3. Table: ExpenseTypes – ExpenseTypeKey, ExpenseType

  4. Table: PaymentMethods – PaymentMethodKey, PaymentMethodDescr


Query Listing 1:

The record set created by the view vwExpenseDetailReport would look like the following:

Expense Type Date Client Amount Payee Description Payment Method Notes
Entertainment 06/13/2005 Smith, Andy J 123.66 Sonics Tickets to game Visa 4499  
Average 06/17/2005 Garcia, Anita 23.65 CJ’s Diner Lunch Ck #6673 Discussed packaging vendors

We now have the data being presented the way we want. If all we wanted to do is display the records then we are ready to go. If we want to allow the user to delete a record we have a big problem though. According to SQL Server 2000, our view doesn’t meet the criteria for an updateable view! This is because we are using the TOP clause and pulling data from multiple tables.

Researching views in SQL Server 2000 Books On-Line, we find that views must meet certain qualifications before they are updateable. In order to run an INSERT, UPDATE or DELETE statement against a view it must meet the following requirements:

  • The select_statement has no aggregate functions in the select list and does not contain the TOP, GROUP BY, UNION (unless the view is a partitioned view as described later in this topic), or DISTINCT clauses. Aggregate functions can be used in a subquery in the FROM clause as long as the values returned by the functions are not modified.

  • The select_statement has no derived columns in the select list. Derived columns are result set columns formed by anything other than a simple column expression, such as using functions or addition or subtraction operators.

  • The FROM clause in the select_statement references at least one table. select_statement must have more than non-tabular expressions, which are expressions not derived from a table. For example, this view is not updatable:

In addition to the requirements for the view to be updateable, the SQL statements operating on the view have to meet certain criteria as well. For instance, to run an INSERT or UPDATE statement against a view, the statement must modify columns from only one of the base tables referenced by the view.

Given the following view created over TableA (col1, col2) and TableB (col1, col2):

The SQL statement

will fail as it references columns from both TableA and TableB.

In order to execute a DELETE statement against a view in SQL Server 2000, the view must meet a different requirement. Again according to Books On-Line, a DELETE statement can reference an updateable view only if the view references a single table in its FROM clause. In the example above, the statement

will fail because the view itself references more than one table.

As I mentioned above, Iron Speed Designer provides some wonderful features for working with views. However, if you want to use a non-updateable view in a Show Table page you will soon find your users complaining about the error message they receive whenever they click Delete. If you are like me at this point you find yourself thinking, "Holy testing error, Batman! I just assumed the Delete button would work!" If our Show Table page was connected to a single table or a view over a single table, this assumption would have been a safe one. In these cases, Iron Speed’s built in DELETE functionality works just fine. Often though what you want to display to your users can’t be drawn from a single table especially with a true Third Normal Form relational data schema. Additionally, facilitating complex WHERE clause logic is easier to accomplish in the backend database than it is in the front end user interface.

Valuable Alternatives
So how do we deal with this problem? There are a few alternatives available to us. One of the choices we have is to use an INSTEAD OF trigger in the backend database. But hey, the purpose here is to figure out how to make Iron Speed Designer do its stuff right? Let’s look at one way we can deal with this issue entirely in Iron Speed Designer by adding a few lines of code to the safe class behind our Show Table page and setting an attribute on the Delete button itself.

While trying to resolve this issue in an application I recently completed (and, yes, all through development I just assumed the delete button worked!), I started poking around in the Iron Speed Designer-generated classes. Now as many of you already know, this in itself is no small task as there are a lot of those puppies in there! So where do we start?

Let’s start by looking at the application documentation Iron Speed Designer generates (not the Base Classes documentation). We are looking for the class that represents the table from which we are trying to delete a record. In our example, this is the Expenses table (see Query Listing 1 above) as our Show Table page is connected to our vwExpenseDetailReport view which primarily pulls from the Expenses table in our database.

Working our way down the tree of class objects in the documentation, we come across an entry called "BaseExpensesTable Class". Bingo! Even better, when we expand that tree node and expand the Methods node, we find a method called "DeleteRecords". Man it's like those Iron Speed engineers have thought of everything!

So now we know that our table class has a delete records method. Looking at the documentation we find this method deletes records using a where clause. Hmmm…if we passed to this method [in the where clause] the primary key of the record we want to delete we should be home free.

Can it be this easy? The answer of course is “Yes” – it is that easy. But how do we employ this DeleteRecord method? As you probably know, .NET is all about objects, properties, methods and...events! If we trap the right event and call the DeleteRecords method on our Expenses table that should do the trick. How can we capture an event? By overriding the OnApplicationEvent method.

Opening up Help in Iron Speed Designer and searching for "override event" we get several hits. Looking at the topic, "Overriding the DeleteRecord event", we learn that we can override the DeleteRecord event in the TableControl class. The example given in Help looks like:

Now we are getting somewhere! Let’s open up the safe class behind our Show Table page and see if we can modify this example from Iron Speed Designer Help to work for us.

What we are looking for is the proper place to insert some code in our safe class. The Iron Speed Designer Help topic told us that the place we want to override the DeleteRecord event is in the TableControl class. In my application, this is in the ApplicationName.PanelControls.ShowVwExpensesTablePage namespace and looks like this:

Now all we need to do is trap the delete record event and run our own code instead of the Iron Speed Designer-generated DeleteRecord event. Remember, we are displaying a view but we need to delete the record from the underlying Expenses table the view draws from. Here I will show you the code you need to insert into the TableControl class to delete a record from a table and not from the view itself. Then we will walk through the code line by line and see how it all works.

Now let’s go into detail in the code and see exactly what is going on here:

This line tells .NET that we are running the code that follows instead of the code that .NET would normally run for a given event.

Next we set up an if…else conditional test to check the event that is being processed. If the event type is DeleteRecord, we want to run our own code in place of Iron Speed’s regular DeleteRecord method which, of course, would run against our view and generate an error. We do this with the following line of code:

If the event being processed is the DeleteRecord event then we need to get the primary key value of the SELECTED record in the table control. How can we do this? If we turn once again to Iron Speed Designer Help and search for "selected row" we get several hits. Our luck is holding up well here as one of the topics is titled, "Handling the Selected Row in a Table". In that example we see the line below does just that for us:

Okay now we have a handle to the row in our table control that the user selected. Now we just need to get the primary key column value for that row so we can pass it in the where clause of our DeleteRecord method. Here we need to remember that we are deleting a record from the Expenses table but the selectedComponent object variable in our code is pointing to a record in the VwExpenses view. So what we will do is get a record object and fill it with data from the selected row via the GetRecordData method of the IRecordComponent class object as so, passing in an argument value of false since we can get by fine with a read-only record…

If we have the actual data values of the selected row from our table control in a record object, all we need now is the primary key value so we can build our where clause. We can do this by referencing the ExpenseKey column of our record object, converting that value to a string with the ToString() method of the column and then converting to an integer that we can pass in our where clause. The line of code below does just this -

Okay, now that we have the primary key value of the selected row we can call the DeleteRecords method on the Expenses table and actually delete the record the user selected in the table control. The where clause we will use simply references the primary key column of the Expenses table.

We are almost home now. Since it is not politically correct (or good programming practice) to leave any transactions open, we need to commit the transaction that the Iron Speed Designer-generated application opened behind the scenes for us to delete the record. To do this, call the CommitTransaction method of the SystemUtils class and pass a value of true for the ‘autoRollback’ argument:

That’s it! Our record is deleted. But where do we go now? Most likely the user will want to see the show table page so she can verify the record was deleted. However if we post back to the page itself we need to make sure the data is refreshed otherwise our deleted record will still show in the cached output. To fix this we do a redirect to our show table page so the page loads with current data.

However if we stop here we have a problem! What about all those page and control events that aren’t of the type DeleteRecord? We need to allow those to complete in a normal fashion. To this end we use the code below:

Now we have successfully handled the DeleteRecord event without interfering with any other events that may be generated by the page or its child controls. Close the if...else conditional test and the application event override function and we are done. Rebuild the application to compile our code and we are all good.

To add a nice extra touch to our application, let’s save our users the grief of clicking Delete only to find they had selected the wrong record. To accomplish this, switch to Design view for the Show Table page and open the page properties dialog. Select the Delete button and go to the Attributes tab. On that tab enter the following attribute name and value –

This tells the Iron Speed Designer-generated application that when a user clicks the delete button we want to pop up a message box and give them a chance to confirm the transaction by clicking “Okay” or back out of it by clicking "Cancel".

Summary
Views provide us with a quick and efficient way to generate an information rich and user friendly display of data. However, as we have seen there are various conditions that must be met in order to update data through a view and often it isn’t possible to have our cake and eat it too. The few lines of code above show how easily and quickly a workaround to a problem can be accomplished using the functionality built into Iron Speed Designer.
About the Author
Michael Weiss
Independent Developer

Michael has over ten years of experience in information technology primarily in the Telecom sector. He has designed, developed and administered data warehouses to 500GB in size, web enabled reporting systems and billing and CRM systems. Additionally, Michael has designed and developed several web enabled OLAP-based Business Intelligence Reporting systems.

Contact the author.



  Privacy Statement