1. Trang chủ
  2. » Công Nghệ Thông Tin

Visual Basic 2005 Design and Development - Chapter 9 pdf

26 204 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 26
Dung lượng 606,19 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Querying the database is generally safe, somany of the applications I’ve written allow the users to execute ad hoc SQL queries.. You can make SQL scripting easier for the users if you pr

Trang 1

There are many ways you can make a program extensible at run-time For example, Chapter 22,

“Reflection,” shows how you can provide add-in functionality by loading compiled DLLs at run-time

One of the most flexible methods for extending an application at run-time is scripting By allowingthe program to execute new code at run-time, you can enable it to do just about anything that youcould do had you written the code ahead of time, at least in theory

This chapter describes several techniques that you can use to add scripting to your applications Itexplains how a program can execute SQL statements or Visual Basic NET code, and how a pro-gram can parse and evaluate arithmetic expressions at run-time

Scripting SafelyScripting is an extremely flexible and powerful way to add functionality to an application

However, like many other powerful and flexible tools, scripting comes with a certain degree ofdanger If you give users the ability to destroy the application or its data, you may very well need

to deal with them doing exactly that Sooner or later you’ll get a call from a customer who hasdropped a key table from the application’s database, used a script to set foreground and back-ground colors to black, or removed key objects from the application’s object model, and wants you

to make everything better Even worse, a disgruntled or dishonest user might modify the data todamage the application or for fraudulent reasons, and you may need to sort out the mess

To reduce the frequency of these types of calls, you should consider the users’ needs and ming skill level Then you should carefully select the scripting capabilities that you provide tomatch

Trang 2

program-For example, if the users only need to make configuration changes such as setting colors and options,you can provide configuration dialogs to make that easier and safer Provide a “Reset to Default” button

to let them undo any really bad changes In this example, scripting isn’t really necessary

Many users understand or can easily learn the basics of SQL Querying the database is generally safe, somany of the applications I’ve written allow the users to execute ad hoc SQL queries This gives the user

an easy way to find particular records and to generate quick one-off reports without requiring the opers to build new reports into the system Most users don’t need to be able to perform more dangerousSQL statements that would let them drop tables, add or delete records, and remove indexes

devel-Even with queries, however, there are occasions when an application cannot allow the users unlimitedfreedom If the application’s data is sensitive, perhaps containing financial or medical information, all ofthe users may not be allowed to see all of the data For example, in a medical application, you mightwant to allow receptionists to see only appointment and insurance information, and not medical records

In that case, you could not allow the users to write their own SQL queries

Actually, if the database provides security at a sufficiently fine-grained level, you might be able to let

users write their own queries Some databases let you define column-level permissions for particular

users Then, if a user tries to select data that he or she is not allowed to see, the database will raise an

error, and the script will fail safely.

You can make SQL scripting easier for the users if you provide some means to store and execute fined queries In some applications I’ve written, supervisors and more experienced users wrote queriesfor other users to execute If you only allow the users to execute predefined queries stored in a database

prede-or a shared directprede-ory that only the supervisprede-ors have permission to edit, then you have pretty good trol over what the users can do

con-Another great place to store predefined queries is as stored procedures That gives you a lot of control

over who can create, modify, and view the queries Stored procedures are saved in the database, so you can modify them without recompiling the application Only developers who have the proper tools and

permissions can view or modify stored procedures, so they are relatively protected Often, databases can give better performance when executing stored procedures than they can when performing queries com- posed by the application’s code All in all, stored procedures are a great place to store database tools for the users to execute.

If the users are less sophisticated, you may want to build tools that help the user compose queries Thesection “Generating Queries” later in this chapter shows one such tool

Users rarely have the skills needed to modify the database safely, so you probably shouldn’t give themthe ability to execute non-query SQL commands such as DROP TABLE That doesn’t mean there’s no point

in executing these scripts, however Sometimes it is useful to give supervisors or application tors tools for performing database maintenance tasks To protect the database, you may want to allowthe users to execute only predefined scripts created by the developers

administra-Stored procedures are a great place to store these maintenance scripts, too.

These sorts of database modification tasks can also be extremely useful for developers Many tions that I’ve worked on modify their data so that you cannot easily perform the same operationsrepeatedly without resetting the database A script that inserts test data into the database can makedevelopment and testing much easier

applica-238

Trang 3

Scripts that manipulate the application’s object model are usually a bit easier to safeguard than SQLscripts If you expose an object model that only allows users to do what they can do by using the userinterface, you don’t need to worry as much about the user’s permissions.

This approach does have some design consequences, however It means that you cannot rely solely onthe user interface to control the user’s access to application features For example, suppose supervisorsshould be able to modify work assignments, but clerks should only be able to view them When a clerkstarts the application, you can hide the menu items that allow the user to edit work assignments, but ifthe object model contains functions to do this, the clerk may be able to use a script to change assign-ments anyway

To prevent this sort of flanking maneuver, the object model’s sensitive methods must always verify thecurrent user’s privileges, even though they might normally be protected by the user interface

If the users don’t have the skills to write their own scripts, or if letting them write scripts is just too gerous, you can still use scripts behind the scenes to provide new functionality You can store scriptswritten by supervisors or developers in a database or protected directory, and then allow the program toexecute them without the user ever seeing the details You might provide a Scripts menu that lists thenames of the scripts and then executes whichever scripts the user selects

dan-If you store the scripts in a database, you have good control over who can look at them dan-If you store the scripts in text files, you can encrypt them if you really don’t want the users peeking.

As a final word of caution, consider how users might inadvertently or intentionally subvert a script,even if your code actually writes the code For example, suppose an application stores user names andpasswords in the Userstable Many programs use code similar to the following to build this query andverify the user’s password:

‘ Verify the user name and password

Private Sub btnVerify_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles btnVerify.Click

‘ Compose the query

Dim user_name As String = txtUserName.TextDim password As String = txtPassword.TextDim query As String = _

“SELECT COUNT(*) FROM Users “ & _

“WHERE UserName = ‘“ & user_name & “‘“ & _

“ AND Password = ‘“ & password & “‘“

Debug.WriteLine(query)

‘ Execute the query

Trym_Connection.Open()Dim cmd As New OleDbCommand(query, m_Connection)Dim result As Integer = CInt(cmd.ExecuteScalar())m_Connection.Close()

If result > 0 ThenMessageBox.Show(“User verified”)Else

MessageBox.Show(“Invalid user”)End If

Trang 4

Catch ex As ExceptionMessageBox.Show(ex.Message, “Query Error”, _MessageBoxButtons.OK, MessageBoxIcon.Exclamation)Finally

If m_Connection.State = ConnectionState.Open Then m_Connection.Close()End Try

End Sub

This code creates and executes a query similar to the following:

SELECT COUNT(*) FROM Users

WHERE UserName = ‘Rod’

AND Password = ‘VbGeek’

If this query returns a value greater than 0, the program assumes the user has a valid password

Now, consider what happens if the user enters the name Rod and the bizarre password shown in the

fol-lowing code:

‘ OR True OR Password = ‘

When the code runs, it produces the following enigmatic SQL statement:

SELECT COUNT(*) FROM Users

WHERE UserName = ‘Rod’

AND Password = ‘’ OR True OR Password = ‘’

This WHEREclause always evaluates to True, so the select statement always returns 1(assuming there isone record with the user name Rod) and the program accepts the user name even though the password

SELECT COUNT(*) FROM Users

WHERE UserName = ‘O’Toole’

AND Password = ‘1337’

Here the extra apostrophe inside the user name confuses the database and the query fails

One solution is to change the code to replace each apostrophe in the input strings with two apostrophes:

Dim user_name As String = txtUserName.Text.Replace(“‘“, “‘’”)

Dim password As String = txtPassword.Text.Replace(“‘“, “‘’”)

Now, the SQL injection attack creates the following SQL query:

SELECT COUNT(*) FROM Users

WHERE UserName = ‘Rod’

AND Password = ‘’’ OR True OR Password = ‘’’

240

Trang 5

Now, the database correctly reads the weird password entered by the user and is not fooled.

The program now creates the following query for user O’Toole:

SELECT COUNT(*) FROM UsersWHERE UserName = ‘O’’Toole’

AND Password = ‘1337’

The database correctly places an apostrophe inside the user name and there’s no problem

Example program ValidateUserdemonstrates both the safe and unsafe version of this code

Scripting is a powerful technique, but it’s not completely without risk Carefully consider the users’needs, skill levels, and permissions Then, you can pick a scripting strategy that gives the most addi-tional flexibility with a reasonable level of risk

Executing SQL StatementsStructured Query Language (SQL) is a relatively easy-to-learn language for interacting with databases Itincludes commands for creating, reading, editing, and deleting data

SQL also includes commands that manipulate the database itself Its commands let you create and droptables, add and remove indexes, and so forth

Visual Studio provides objects that interact with databases by executing SQL statements, so providingscripting capabilities is fairly easy

The following section explains how a program can execute queries written by the user The section afterthat shows how a program can provide a tool that makes building queries easier and safer The final sec-tion dealing with SQL scripts shows how a program can execute more general SQL statements to modifyand delete data, and to alter the database’s structure

Note that these sections provide only brief coverage of database programming as it applies to scripting

in Visual Basic, and they omit lots of details For more in-depth coverage of database programming, see

a book about database programming in Visual Basic NET, such as my book Visual Basic NET Database

Programming (Indianapolis: Que, 2002).

‘ Select the data

Dim data_table As New DataTable(“Books”)Dim da As New OleDbDataAdapter(query, m_Connection)

‘ Get the data

Trang 6

DataSourceproperty to the DataTableand closing the database connection.

Example program UserSqlQueryuses similar code to execute ad hoc queries It provides some tional error-handling code, and does some extra work to format the DataGridView’s columns (forexample, it right-justifies numbers and dates) Download the example to see how the code works Figure9-1 shows the program in action

addi-Figure 9-1: Program UserSqlQuerylets the user execute ad hoc SQL queries

The program uses a combo box to let the user enter a query, or select from a list of previously definedqueries When the code successfully executes a query, the program saves it in the combo box’s list and inthe Registry so that it will be available to the user later

Program UserSqlQueryalso allows the user to select the fields displayed by the DataGridViewcontrol.When you select the Data menu’s Select Fields command, the program displays the dialog shown inFigure 9-2 The dialog lists the fields returned by the query, and lets the user select the ones that should

be visible in the grid

242

Trang 7

Figure 9-2: Program UserSqlQuery

lets the user select the fields it displays in its grid

The following code shows how the Select Fields dialog works:

Imports System.Data.OleDbPublic Class dlgSelectFields

‘ The DataGridView on the main form

Public TheDataGridView As DataGridView = Nothing

‘ Load the list of database fields

Private Sub dlgSelectFields_Load(ByVal sender As Object, _ByVal e As System.EventArgs) Handles Me.Load

‘ Make sure TheDataGridView has been initialized

Debug.Assert(TheDataGridView IsNot Nothing, “TheDataGridView is Nothing”)

‘ Set properties (Done here so it’s easier to find.)clbFields.CheckOnClick = True

‘ Fill the checked list box with the fields

clbFields.Items.Clear()For i As Integer = 0 To TheDataGridView.Columns.Count - 1clbFields.Items.Add(TheDataGridView.Columns(i).HeaderText)Dim checked As Boolean = TheDataGridView.Columns(i).VisibleclbFields.SetItemChecked(i, checked)

Next iEnd Sub

‘ Apply the user’s selections to the DataGridView

Private Sub btnOk_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles btnOk.ClickFor i As Integer = 0 To TheDataGridView.Columns.Count - 1TheDataGridView.Columns(i).Visible = clbFields.GetItemChecked(i)Next i

End SubEnd Class

Trang 8

The main program sets the form’s TheDataGridViewvariable to a reference to the DataGridViewtrol before displaying the form.

con-When the dialog loads, the code loops through the grid’s Columnscollection, adding each column’sheader text to the dialog’s CheckedListBoxcontrol clbFields It checks an item if the correspondinggrid column is currently visible

If the user clicks the OK button, the dialog again loops through grid’s columns, this time setting a umn’s Visibleproperty to Trueif the corresponding item is checked in the dialog’s list box (You candownload this example at www.vb-helper.com/one_on_one.htm.)

>=, IS NULL, LIKE, and so forth) in the middle column, and enters a value string in the right column

Figure 9-3: Program SelectCriteriauses this dialog to build SQL queries

When the user clicks OK, the program uses the selections on the dialog to build a SQL query The tions shown in Figure 9-3 generate the following SQL statement:

selec-SELECT * FROM Books

WHERE Title LIKE ‘%Visual Basic’

Public Class dlgCriteria

‘ The DataGridView on the main form

244

Trang 9

Public TheDataGridView As DataGridView = Nothing

‘ The collection of criteria

Public TheCriteria As Collection = Nothing

‘ Delimiters for the field choices

Private m_Delimiters As Collection

‘ Load the list of database fields

Private Sub dlgSelectFields_Load(ByVal sender As Object, _ByVal e As System.EventArgs) Handles Me.Load

‘ Make sure TheDataGridView and TheCriteria have been initialized

Debug.Assert(TheDataGridView IsNot Nothing, “TheDataGridView is Nothing”)Debug.Assert(TheCriteria IsNot Nothing, “TheCriteria is Nothing”)

‘ Get the cell’s template object

Dim dgv_cell As DataGridViewCell = dgvCriteria.Columns(0).CellTemplateDim combo_cell As DataGridViewComboBoxCell = _

DirectCast(dgv_cell, DataGridViewComboBoxCell)

‘ Make a list of the fields

combo_cell.Items.Clear()m_Delimiters = New CollectionFor i As Integer = 0 To TheDataGridView.Columns.Count - 1

‘ Add the name to the combo cell

Dim field_name As String = TheDataGridView.Columns(i).Namecombo_cell.Items.Add(field_name)

‘ Get an appropriate delimiter for the data type

Dim delimiter As String = “”

If TheDataGridView.Columns(i).ValueType Is GetType(String) Thendelimiter = “‘“

ElseIf TheDataGridView.Columns(i).ValueType Is GetType(Date) Then

‘ Note that you need to handle dates differently in SQL Server.delimiter = “#”

End Ifm_Delimiters.Add(delimiter, field_name)Next i

‘ Display current criteria

dgvCriteria.RowCount = TheCriteria.Count + 1For r As Integer = 0 To TheCriteria.Count - 1Dim condition As Criterion = DirectCast(TheCriteria(r + 1), Criterion)dgvCriteria.Rows(r).Cells(0).Value = condition.FieldName

dgvCriteria.Rows(r).Cells(1).Value = condition.OpdgvCriteria.Rows(r).Cells(2).Value = condition.ValueNext r

End Sub

‘ Create the new Criteria collection

Private Sub btnOk_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles btnOk.Click

‘ Verify that each row has a field and operator

For r As Integer = 0 To dgvCriteria.RowCount - 2

Trang 10

If dgvCriteria.Rows(r).Cells(0).Value Is Nothing ThenMessageBox.Show( _

“You must select a field for every row”, _

“Missing Field”, _MessageBoxButtons.OK, _MessageBoxIcon.Exclamation)dgvCriteria.CurrentCell = dgvCriteria.Rows(r).Cells(0)dgvCriteria.Select()

Me.DialogResult = Windows.Forms.DialogResult.NoneExit Sub

Me.DialogResult = Windows.Forms.DialogResult.NoneExit Sub

End IfNext r

‘ Make the new criteria collection

TheCriteria = New CollectionFor r As Integer = 0 To dgvCriteria.RowCount - 2Dim field_name As String = _

DirectCast(dgvCriteria.Rows(r).Cells(0).Value, String)Dim delimiter As String = _

DirectCast(m_Delimiters(field_name), String)Dim op As String = _

DirectCast(dgvCriteria.Rows(r).Cells(1).Value, String)Dim value As String = _

DirectCast(dgvCriteria.Rows(r).Cells(2).Value, String)TheCriteria.Add(New Criterion(field_name, op, value, _delimiter))

Next rEnd SubEnd Class

When the dialog loads, it gets the template cell for the dialog’s first grid column This cell acts as a plate to define other cells in the column so that when the program defines the field names in its drop-down list, it defines the values for every drop-down in this column

tem-The program loops through the main program’s DataGridViewcolumns, adding each column’s name tothe drop-down list It saves a corresponding delimiter (apostrophe for strings, # for dates, an emptystring for other data types) for the column in the m_Delimiterscollection

The allowed operators (<, <=, =, >=, >, LIKE, IS NULL, and IS NOT NULL) were set for the second umn at design time.

col-246

Trang 11

The program then loops through the collection named TheCriteria, which contains Criterionobjectsrepresenting the program’s current selection criteria It uses the objects’ properties to set field names,operators, and values in the dialog’s grid.

When the user clicks the dialog’s OK button, the program first verifies that every entered row has a blank field name and operator It then loops through the grid’s rows, builds Criterionobjects for each,and adds them to a new TheCriteriacollection

non-The following code shows the Criterionclass Its main purpose is just to store a field name, operator,and delimiter It includes a couple of constructors to make creating objects easier The ToStringfunc-tion makes it easier to save objects in the Registry

‘ Represent a condition such as FirstName >= ‘Stephens’

Public Class CriterionPublic FieldName As StringPublic Op As StringPublic Value As StringPublic Delimiter As StringPublic Sub New(ByVal new_field_name As String, ByVal new_op As String, _ByVal new_value As String, ByVal new_delimiter As String)

FieldName = new_field_name

Op = new_opValue = new_valueDelimiter = new_delimiterEnd Sub

‘ Initialize with tab-delimited values

Public Sub New(ByVal txt As String)Dim items() As String = txt.Split(CChar(vbTab))FieldName = items(0)

Op = items(1)Value = items(2)Delimiter = items(3)End Sub

‘ Return tab-delimited values

Public Overrides Function ToString() As StringReturn FieldName & vbTab & Op & vbTab & Value & vbTab & DelimiterEnd Function

End Class

Like UserSqlQuery, example program SelectCriterialets the user select the columns that should

be displayed in the main program’s DataGridViewcontrol (You can download this example at

www.vb-helper.com/one_on_one.htm.)

Running Commands

Though executing ad hoc queries is handy for users, developers need more powerful tools Often, it’shandy to execute more general SQL commands that add, modify, or delete data, or that modify thedatabase’s structure Fortunately, Visual Basic’s database tools make this relatively straightforward

Trang 12

The ExecuteNonQueryfunction shown in the following code executes a SQL command and returns asuccess or failure message It simply creates an OleDbCommandobject associated with the command andthe database connection and then executes it.

‘ Execute a non-query command and return a success or failure string

Public Function ExecuteNonQuery(ByVal conn As OleDbConnection, _

ByVal txt As String) As String

Try

‘ Make and execute the command

Dim cmd As New OleDbCommand(txt, conn)cmd.ExecuteNonQuery()

Return “> Ok”

Catch ex As ExceptionReturn “*** Error executing command ***” & vbCrLf & ex.MessageEnd Try

End Function

The following code shows a function that executes a query It creates an OleDbCommandobject for thequery and calls its ExecuteReadercommand to run the query and get a data reader to process theresults It loops through the reader’s columns, adding their names to a result string It then uses thereader to loop through the returned rows and adds the rows’ field values to the result string

‘ Execute a query command and return

‘ the results or failure string

Public Function ExecuteQuery(ByVal conn As OleDbConnection, _

ByVal query As String) As String

Try

‘ Make and execute the command

Dim cmd As New OleDbCommand(query, conn)Dim reader As OleDbDataReader = cmd.ExecuteReader()

‘ Display the column names

Dim row_txt As String = “”

For c As Integer = 0 To reader.FieldCount - 1row_txt &= “, “ & reader.GetName(c)Next c

‘ Remove the initial “, “

Dim txt As String = “ -” & vbCrLf & _row_txt.Substring(2) & vbCrLf & “ -” & vbCrLf

‘ Display the results

Do While reader.Read()row_txt = “”

For c As Integer = 0 To reader.FieldCount - 1row_txt &= “, “ & reader.Item(c).ToString()Next c

‘ Remove the initial “, “

txt &= row_txt.Substring(2) & vbCrLfLoop

reader.Close()Return txtCatch ex As ExceptionReturn “*** Error executing SELECT statement ***” & vbCrLf & _

248

Trang 13

ex.MessageEnd Try

End Function

Example program ExecuteSqlScriptuses these functions to run general SQL scripts It uses the lowing to break a script apart and call functions ExecuteNonQueryand ExecuteQueryto process thepieces

fol-‘ Execute the SQL script

Private Sub btnRun_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles btnRun.Click

‘ Open the connection

m_Connection.Open()

‘ Break the script into semi-colon delimited commands

Dim commands() As String = Split(txtScript.Text, “;”)

‘ Execute each command

Dim results As String = “”

For i As Integer = 0 To commands.Length - 1

‘ Clean up the command to see if it’s non-blank

Dim cmd As String = _commands(i).Replace(vbCr, “ “).Replace(vbLf, “ “).Trim()

‘ Execute only non-blank commands

If cmd.Length > 0 ThenDebug.WriteLine(commands(i))

‘ Display the command

results &= commands(i) & vbCrLftxtResults.Text = resultstxtResults.Select(results.Length, 0)txtResults.ScrollToCaret()

txtResults.Refresh()

‘ See if this is a SELECT command

If cmd.ToUpper.StartsWith(“SELECT”) Then

‘ Execute the query

results = results & ExecuteQuery(m_Connection, commands(i))Else

‘ Execute the non-query command

results = results & ExecuteNonQuery(m_Connection, commands(i))End If

results &= vbCrLf & “==========” & vbCrLftxtResults.Text = results

txtResults.Select(results.Length, 0)txtResults.ScrollToCaret()

txtResults.Refresh()End If

Next i

‘ Close the connection

Ngày đăng: 14/08/2014, 11:20

TỪ KHÓA LIÊN QUAN