dat402
DESCRIPTION
Advanced dot netTRANSCRIPT
DAT402:DAT402:Advanced ADO.NETAdvanced ADO.NET
Michael PizzoMichael PizzoSoftware ArchitectSoftware ArchitectWebData TeamWebData TeamMicrosoft CorporationMicrosoft Corporation
AgendaAgenda
Common TechniquesCommon Techniques Optimizing PerformanceOptimizing Performance Security ConsiderationsSecurity Considerations QuestionsQuestions
AgendaAgenda
Common TechniquesCommon Techniques Updating from a DataSetUpdating from a DataSet Server Cursors and ADO.NETServer Cursors and ADO.NET Using the DataSet as a CacheUsing the DataSet as a Cache Controlling how XML is generatedControlling how XML is generated Working with Identity ColumnsWorking with Identity Columns Handling Large ResultSetsHandling Large ResultSets Join Queries against a DataSetJoin Queries against a DataSet RecordsetRecordsetDatasetDataset
Optimizing PerformanceOptimizing Performance Security ConsiderationsSecurity Considerations
Updating From A DataSetUpdating From A DataSet
Use a DataAdapterUse a DataAdapter Typically one per table to be updatedTypically one per table to be updated Specify Insert, Update, Delete commandsSpecify Insert, Update, Delete commands
Use CommandBuilder for AdHoc queriesUse CommandBuilder for AdHoc queries Designers, toolsDesigners, tools
Call Update(), passing in Call Update(), passing in DataSet/DataTable to be updatedDataSet/DataTable to be updated No persistent connection between No persistent connection between
DataAdapter and DataSetDataAdapter and DataSet Can use different DataAdapters for Fill and UpdateCan use different DataAdapters for Fill and Update Use ExtendedProperties to pass datasource Use ExtendedProperties to pass datasource
informationinformation
Specifying Update CommandsSpecifying Update Commands
Associate parameters with DataSet ColumnsAssociate parameters with DataSet Columns Specify SourceColumn propertySpecify SourceColumn property Parameter values will be set using values from the Dataset Parameter values will be set using values from the Dataset
for each Insert, Update, or Deletefor each Insert, Update, or Delete
'Specify Delete Command'Specify Delete CommandDim delete As New SqlCommand("DeleteOrder", cnn)Dim delete As New SqlCommand("DeleteOrder", cnn)delete.CommandType = CommandType.StoredProceduredelete.CommandType = CommandType.StoredProcedure
'Create a Parameter and associate it with Dataset column'Create a Parameter and associate it with Dataset columnDim param As New SqlParameter("@OrderID", SqlDbType.Int)Dim param As New SqlParameter("@OrderID", SqlDbType.Int)param.SourceColumn = "OrderID"param.SourceColumn = "OrderID"delete.Parameters.Add(param)delete.Parameters.Add(param)
'Set as the Delete Command'Set as the Delete Commandadapter.DeleteCommand = deleteadapter.DeleteCommand = delete
Dim adapter As New SqlDataAdapter()Dim adapter As New SqlDataAdapter()Dim delete As New SqlCommand("DeleteOrder", cnn)Dim delete As New SqlCommand("DeleteOrder", cnn)delete.CommandType = CommandType.StoredProceduredelete.CommandType = CommandType.StoredProceduredelete.Parameters.Add("@OrderID", SqlDbType.Int).SourceColumn = "OrderID"delete.Parameters.Add("@OrderID", SqlDbType.Int).SourceColumn = "OrderID"adapter.DeleteCommand = deleteadapter.DeleteCommand = deleteDim insert As New SqlCommand("AddOrder", cnn)Dim insert As New SqlCommand("AddOrder", cnn)insert.CommandType = CommandType.StoredProcedureinsert.CommandType = CommandType.StoredProcedureinsert.Parameters.Add("@OrderID", SqlDbType.Int).SourceColumn = "OrderID"insert.Parameters.Add("@OrderID", SqlDbType.Int).SourceColumn = "OrderID"insert.Parameters.Add("@CustD", SqlDbType.Int).SourceColumn = "CustomerID"insert.Parameters.Add("@CustD", SqlDbType.Int).SourceColumn = "CustomerID"insert.Parameters.Add("@Date", SqlDbType.DateTime).Value = DateTime.Nowinsert.Parameters.Add("@Date", SqlDbType.DateTime).Value = DateTime.Nowadapter.InsertCommand = insertadapter.InsertCommand = insertDim update As New SqlCommand("UpdateOrder", cnn)Dim update As New SqlCommand("UpdateOrder", cnn)update.CommandType = CommandType.StoredProcedureupdate.CommandType = CommandType.StoredProcedureupdate.Parameters.Add("@OrderID",SqlDbType.Int).SourceColumn="OrderID"update.Parameters.Add("@OrderID",SqlDbType.Int).SourceColumn="OrderID"update.Parameters.Add("@CustD",SqlDbType.Int).SourceColumn="CustomerID"update.Parameters.Add("@CustD",SqlDbType.Int).SourceColumn="CustomerID"adapter.UpdateCommand = updateadapter.UpdateCommand = updateadapter.Update(ordersTable)adapter.Update(ordersTable)
Updating ExampleUpdating Example
Optimistic ConcurrencyOptimistic Concurrency Specify Original values or RowVersion columnSpecify Original values or RowVersion column
Original values for changes made to data readOriginal values for changes made to data read RowVersion for any changes made to rowRowVersion for any changes made to row
Set SourceVersion for original valuesSet SourceVersion for original values
Account for Nulls in Where clause:Account for Nulls in Where clause:
Add additional logic in Stored Procedure for Add additional logic in Stored Procedure for handling concurrency and conflictshandling concurrency and conflicts
WHERE ((Country ISNULL) AND (@Country ISNULL)) OR (Country=@Country)WHERE ((Country ISNULL) AND (@Country ISNULL)) OR (Country=@Country)
Dim update As New SqlCommand("Update Customers (Name) Values " & _ Dim update As New SqlCommand("Update Customers (Name) Values " & _ "(@Name) Where Name=@OldName AND CustID=@ID",cnn)"(@Name) Where Name=@OldName AND CustID=@ID",cnn)
update.Parameters.Add("@Name",SqlDbType.VarChar).SourceColumn = "Name"update.Parameters.Add("@Name",SqlDbType.VarChar).SourceColumn = "Name"update.Parameters.Add("@CustID",SqlDbType.Int).SourceColumn = "CustomerID"update.Parameters.Add("@CustID",SqlDbType.Int).SourceColumn = "CustomerID"Dim param = update.Parameters.Add("@OldName",SqlDbType.VarChar)Dim param = update.Parameters.Add("@OldName",SqlDbType.VarChar)param.SourceColumn = "Name"param.SourceColumn = "Name"param.SourceVersion = DataRowVersion.Originalparam.SourceVersion = DataRowVersion.Original
Server Cursors And ADO.NETServer Cursors And ADO.NET
DataReader versus DataSetDataReader versus DataSet DataReader is a Forward Only/Read Only "Server Cursor"DataReader is a Forward Only/Read Only "Server Cursor" Requests Data from Server as read on ClientRequests Data from Server as read on Client
Buffered in packets at network levelBuffered in packets at network level Holds state on the Server until closedHolds state on the Server until closed
Pessimistic ConcurrencyPessimistic Concurrency Locking records when read ensures updates don't fail due to Locking records when read ensures updates don't fail due to
concurrency violationsconcurrency violations Kills scalability of applicationKills scalability of application Supported in ADO.NET through transactionsSupported in ADO.NET through transactions
Scrollable Server CursorsScrollable Server Cursors Holds State on ServerHolds State on Server Round-trip per fetch/updateRound-trip per fetch/update Generally better handled in DataSet or Stored ProcedureGenerally better handled in DataSet or Stored Procedure Supported in ADO.NET through Cursor DML commandsSupported in ADO.NET through Cursor DML commands
'Start a transaction and set on Select command'Start a transaction and set on Select commandDim connect = adapter.SelectCommand.ConnectionDim connect = adapter.SelectCommand.Connectionconnect.Open()connect.Open()Dim tran = connect.BeginTransaction(IsolationLevel.Serializable)Dim tran = connect.BeginTransaction(IsolationLevel.Serializable)adapter.SelectCommand.Transaction = tranadapter.SelectCommand.Transaction = tran'Fill DataSet and make changes'Fill DataSet and make changesadapter.Fill(ds, "Employees")adapter.Fill(ds, "Employees")Dim employee As DataRowDim employee As DataRowFor Each employee In ds.Tables("Employees").RowsFor Each employee In ds.Tables("Employees").Rows employee("Salary") = employee("Salary") * 1.1employee("Salary") = employee("Salary") * 1.1NextNext'Set the connection and transaction for the update command'Set the connection and transaction for the update commandadapter.UpdateCommand.Connection = connectadapter.UpdateCommand.Connection = connectadapter.UpdateCommand.Transaction = tranadapter.UpdateCommand.Transaction = tranadapter.Update(ds,"Employees")adapter.Update(ds,"Employees")tran.Commit()tran.Commit()connect.Close()connect.Close()
Pessimistic ConcurrencyPessimistic Concurrency
' Declare and open cursor' Declare and open cursorDim cmd As New SqlCommand(Nothing, conn)Dim cmd As New SqlCommand(Nothing, conn)cmd.CommandText = “DECLARE mycursor SCROLLABLE CURSOR FOR select * from Customers”cmd.CommandText = “DECLARE mycursor SCROLLABLE CURSOR FOR select * from Customers”cmd.ExecuteNonQuery()cmd.ExecuteNonQuery()cmd.CommandText = “OPEN mycursor”cmd.CommandText = “OPEN mycursor”cmd.ExecuteNonQuery()cmd.ExecuteNonQuery()
' Read from cursor' Read from cursorDim dr As SqlDataReaderDim dr As SqlDataReadercmd.CommandText = “FETCH NEXT FROM mycursor”cmd.CommandText = “FETCH NEXT FROM mycursor”While(True)While(True)
dr =cmd.ExecuteReader()dr =cmd.ExecuteReader()if (dr.Read() = false) Then Exit Whileif (dr.Read() = false) Then Exit WhileConsole.WriteLine("CompanyName is " & dr("CompanyName"))Console.WriteLine("CompanyName is " & dr("CompanyName"))dr.Close()dr.Close()
End WhileEnd While
' Update fifth row' Update fifth rowcmd.CommandText = “FETCH ABSOLUTE 5 FROM mycursor”cmd.CommandText = “FETCH ABSOLUTE 5 FROM mycursor”cmd.ExecuteNonQuery()cmd.ExecuteNonQuery()cmd.CommandText = “UPDATE Customers set FirstName = ‘Bill’ WHERE CURRENT OF mycursor”cmd.CommandText = “UPDATE Customers set FirstName = ‘Bill’ WHERE CURRENT OF mycursor”cmd.ExecuteNonQuery()cmd.ExecuteNonQuery()
' Close the cursor' Close the cursorcmd.CommandText = “CLOSE mycursor; DEALLOCATE mycursor”cmd.CommandText = “CLOSE mycursor; DEALLOCATE mycursor”cmd.ExecuteNonQuery()cmd.ExecuteNonQuery()
Scrollable Server CursorsScrollable Server Cursors
Using The Dataset As A CacheUsing The Dataset As A Cache
Function GetCategories() As DataSetFunction GetCategories() As DataSet'See if DataSet exists in Cache'See if DataSet exists in CacheDim categories As DataSet = Cache("CategoriesDS")Dim categories As DataSet = Cache("CategoriesDS")if (categories Is Nothing) ' not in cacheif (categories Is Nothing) ' not in cache
'Create DataAdapter and Load DataSet'Create DataAdapter and Load DataSetDim adapter As new SqlDataAdapter( _Dim adapter As new SqlDataAdapter( _
"Select CategoryName from Categories",cnn)"Select CategoryName from Categories",cnn)adapter.Fill(categories)adapter.Fill(categories)'Put Categories Dataset into Cache'Put Categories Dataset into CacheCache("CategoriesDS")=categoriesCache("CategoriesDS")=categories
End IfEnd Ifreturn categoriesreturn categories
End FunctionEnd Function
DataSet optimized for multi-threaded read accessDataSet optimized for multi-threaded read access Can put in ASP.NET cacheCan put in ASP.NET cache Doesn't take locksDoesn't take locks Need to synchronize writesNeed to synchronize writes Clone, Update, and replaceClone, Update, and replace
Controlling How The XML Is GeneratedControlling How The XML Is Generated DataSet lets you control how XML is generatedDataSet lets you control how XML is generated Name, Namespace properties on DataSet, DataTable, DataColumnName, Namespace properties on DataSet, DataTable, DataColumn MappingType property on DataColumn defines how data is writtenMappingType property on DataColumn defines how data is written
Element, Attribute, SimpleType, HiddenElement, Attribute, SimpleType, Hidden Nested Property on DataRelation controls how children are writtenNested Property on DataRelation controls how children are written
' Write out CustomerID, OrderID as Attributes' Write out CustomerID, OrderID as Attributesds.Tables("Customers").Columns("CustomerID").ColumnMapping = MappingType.Attributeds.Tables("Customers").Columns("CustomerID").ColumnMapping = MappingType.Attributeds.Tables("Orders").Columns("OrderID").ColumnMapping = MappingType.Attributeds.Tables("Orders").Columns("OrderID").ColumnMapping = MappingType.Attribute
' Write out Orders as children of Customers' Write out Orders as children of Customersds.Relations("cust_orders").Nested = Trueds.Relations("cust_orders").Nested = True <?xml version="1.0" standalone="yes"?><?xml version="1.0" standalone="yes"?>
<CustomerOrders><CustomerOrders> <Customers CustomerID="GROSR"><Customers CustomerID="GROSR"> <ContactName>Manuel Pereira</ContactName><ContactName>Manuel Pereira</ContactName> <Orders OrderID="10268"><Orders OrderID="10268"> <CustomerID>GROSR</CustomerID><CustomerID>GROSR</CustomerID> <OrderDate>1996-07-30</OrderDate><OrderDate>1996-07-30</OrderDate> </Orders></Orders> </Customers></Customers></CustomerOrders></CustomerOrders>
Working With Identity Working With Identity ColumnsColumns
Issue: Primary Keys must be unique across Issue: Primary Keys must be unique across consumers in order to merge back to consumers in order to merge back to databasedatabase
Solution #1: Use a GUID as the Primary Solution #1: Use a GUID as the Primary Key where possibleKey where possible Can be generated on the Client or ServerCan be generated on the Client or Server
Guaranteed to be uniqueGuaranteed to be unique
Doesn’t change when updated to the ServerDoesn’t change when updated to the Server No fixup required for child rowsNo fixup required for child rows
Issue: Primary Keys must be unique across consumers in Issue: Primary Keys must be unique across consumers in order to merge back to databaseorder to merge back to database
Solution #2: If you are stuck with an AutoIncrement Solution #2: If you are stuck with an AutoIncrement Primary Key…Primary Key… First, make sure Client value doesn't conflict with Server valueFirst, make sure Client value doesn't conflict with Server value
Set AutoIncrement Seed, Step to -1 on DataSetSet AutoIncrement Seed, Step to -1 on DataSet To update values in Dataset, select back the ID in your To update values in Dataset, select back the ID in your
InsertCommandInsertCommand Update the inserted row with the new valueUpdate the inserted row with the new value
Insert Parent rows before Child RowsInsert Parent rows before Child Rows Use UpdateRule.CascadeUse UpdateRule.Cascade
If you are merging with the original DataSet, prevent If you are merging with the original DataSet, prevent AcceptChanges from being called on Inserted rowAcceptChanges from being called on Inserted row Specify SkipCurrentRow in OnRowUpdated EventhandlerSpecify SkipCurrentRow in OnRowUpdated Eventhandler
Working With Identity Working With Identity ColumnsColumns
Sub UpdateData(table As DataTable)
' Set InsertCommand with returned Identity Dim insert As New SqlCommand( _
"Insert into Orders(OrderID,Date) values @OrderID, @Date)" _& ";Select SCOPE_IDENTITY() as OrderID",cnn)
insert.Parameters.Add("@OrderID",SqlDbType.Int).SourceColumn="OrderID" insert.Parameters.Add("@Date",SqlDbType.DateTime).Value = DateTime.Now adapter.InsertCommand = insert
' Set UpdateRowSource and Register RowUpdatedEventHandler insert.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord AddHandler adapter.RowUpdated, _
New SqlRowUpdatedEventHandler(AddressOf myHandler)
DataAdapter.Update(table)End Sub
Shared Sub myHandler(adapter as Object, e As SqlRowUpdatedEventArgs) ' Don't call AcceptChangese.Status = UpdateStatus.SkipCurrentRow
End Sub
Inserting AutoIncrement ValuesInserting AutoIncrement Values
Refreshing Data In The DataSetRefreshing Data In The DataSet
To update DataSet with current To update DataSet with current values from the Database valuesvalues from the Database values Specify a Primary Key on the TableSpecify a Primary Key on the Table Use Adapter.Fill()Use Adapter.Fill()
Fill will update existing values if you have Fill will update existing values if you have a Primary keya Primary key
To update original values but To update original values but preserve changespreserve changes Fill a new DataSet and use Fill a new DataSet and use
DataSet.Merge() with DataSet.Merge() with PreserveChanges=truePreserveChanges=true
Handling Large ResultSetsHandling Large ResultSets Select just the data the user needsSelect just the data the user needs
User rarely wants to scroll through >100 recordsUser rarely wants to scroll through >100 records Call Cancel() on command to dispose of data Call Cancel() on command to dispose of data
beyond what is readbeyond what is read Page through large resultsPage through large results
Use TOP n in SQL ServerUse TOP n in SQL Server Use MaxRows for other databasesUse MaxRows for other databasesPaging by Ordinal range (i.e., records 91-100)Paging by Ordinal range (i.e., records 91-100)
Paging by value range (i.e., M-N)Paging by value range (i.e., M-N) Parameterized Where clauseParameterized Where clause
SELECT TOP 10 * FROM SELECT TOP 10 * FROM (SELECT TOP 100 ProductName, UnitPrice FROM Products ORDER BY UnitPrice ASC) AS Products(SELECT TOP 100 ProductName, UnitPrice FROM Products ORDER BY UnitPrice ASC) AS ProductsORDER BY UnitPrice DESCORDER BY UnitPrice DESC
SELECT TOP 10 ProductName, UnitPrice FROM Products SELECT TOP 10 ProductName, UnitPrice FROM Products WHERE ProductName > @PreviousName ORDER BY ProductName ASC AS ProductsWHERE ProductName > @PreviousName ORDER BY ProductName ASC AS Products
Join Queries Against DataSetJoin Queries Against DataSet Use Relations to navigate from parent to childUse Relations to navigate from parent to child
Restriction: Can’t limit parent based on child valuesRestriction: Can’t limit parent based on child values Use XmlDataDocumentUse XmlDataDocument
X/Path queries can be hierarchicalX/Path queries can be hierarchical
Can get DataRows corresponding to returned ElementsCan get DataRows corresponding to returned ElementsDim nodeslist = xmlData.SelectNodes("//Customers/Orders[@State=WA]")
Dim customer, order As DataRowFor Each customer in CustomerTable.Select("State='WA'")
Console.WriteLine("Customer: " & customer("ContactName"))For Each order in customer.GetChildRows("custord")
Console.WriteLine("Order Amount = " & order("Amount"))
NextNext
Dim node As XmlNodeDim customer as DataRowFor Each node in nodelist
customer = xmlData.GetRowFromElement(node)Next
RecordSet RecordSet DataSetDataSet Getting Data From a RecordSetGetting Data From a RecordSet
Use the OleDbDataAdapter.Fill() methodUse the OleDbDataAdapter.Fill() method
Persist the RecordSet as XML and load into the DataSetPersist the RecordSet as XML and load into the DataSet Use an XSL/T for best resultsUse an XSL/T for best results
Getting Data From a DataSet to a RecordSetGetting Data From a DataSet to a RecordSet Write an XSL Transform to do the conversionWrite an XSL Transform to do the conversion
We hope to provide one of these in the futureWe hope to provide one of these in the future Walk through the DataSet and generate the XDR description Walk through the DataSet and generate the XDR description
then write out the XML in attribute-centric formatthen write out the XML in attribute-centric format Sample coming soon…Sample coming soon…
Dim rs As New ADODB.Recordset() Dim rs As New ADODB.Recordset() rs.Open("Select * from Orders", "Data Source=localhost;Integrated Security=true")rs.Open("Select * from Orders", "Data Source=localhost;Integrated Security=true")Dim adapter As New OleDbDataAdapter()Dim adapter As New OleDbDataAdapter()adapter.Fill(ds, rs, "Orders")adapter.Fill(ds, rs, "Orders")
Dim ds As New DataSet()Dim ds As New DataSet()ds.ReadXml("rsOrders.xml)ds.ReadXml("rsOrders.xml)
AgendaAgenda
Common TechniquesCommon Techniques Optimizing PerformanceOptimizing Performance
Optimizing Data RetrievalOptimizing Data Retrieval Using Schema at Design TimeUsing Schema at Design Time Using BatchesUsing Batches Looking Up Values in the DataSetLooking Up Values in the DataSet Provider specific OptimizationsProvider specific Optimizations
Security ConsiderationsSecurity Considerations QuestionsQuestions
Optimizing Data RetrievalOptimizing Data Retrieval Use ExecuteNonQuery if no results to be returnedUse ExecuteNonQuery if no results to be returned
DDL commands,Inserts, Updates, DeletesDDL commands,Inserts, Updates, Deletes Non-result returning Stored Procedures (parameters ok)Non-result returning Stored Procedures (parameters ok)
Return single set of values using Output ParametersReturn single set of values using Output Parameters
Dim numRowsAffected As Integer = cmd.ExecuteNonQuery()Dim numRowsAffected As Integer = cmd.ExecuteNonQuery()
cmd.CommandText = _cmd.CommandText = _ "Select @ContactName = ContactName From Customers where CustomerID = 'GROSR'""Select @ContactName = ContactName From Customers where CustomerID = 'GROSR'" Dim param = cmd.Parameters.Add("@ContactName",SqlDbType.VarChar,25)Dim param = cmd.Parameters.Add("@ContactName",SqlDbType.VarChar,25) param.Direction = ParameterDirection.Outputparam.Direction = ParameterDirection.Output cmd.ExecuteNonQuery()cmd.ExecuteNonQuery() Console.WriteLine("Contact Name = "& param.Value)Console.WriteLine("Contact Name = "& param.Value)
Efficiently Retrieving BLOBsEfficiently Retrieving BLOBs
Use CommandBehavior.SequentialAccessUse CommandBehavior.SequentialAccess Must retrieve columns in orderMust retrieve columns in order No buffering of column valuesNo buffering of column values
Dim cmd As New SqlCommand( "Select * from Employees", cnn)Dim cmd As New SqlCommand( "Select * from Employees", cnn)Dim results = cmd.ExecuteReader(CommandBehavior.SequentialAccess)Dim results = cmd.ExecuteReader(CommandBehavior.SequentialAccess)Dim i As IntegerDim i As IntegerWhile(results.Read()) While(results.Read())
For i=0 to results.FieldCountFor i=0 to results.FieldCountConsole.Write("\t "& results(i))Console.Write("\t "& results(i))Console.WriteLine()Console.WriteLine()
NextNextEnd WhileEnd While
Use Metadata At Design Time Use Metadata At Design Time
DataReaderDataReader Strongly Typed Ordinal accessorsStrongly Typed Ordinal accessorsDim name As String = dr.GetString(0)Dim name As String = dr.GetString(0)
DataSetDataSet Load, don't infer, schemaLoad, don't infer, schema
Data AdapterData Adapter Don’t use CommandBuilderDon’t use CommandBuilder
Specify insert,update,delete commands when knownSpecify insert,update,delete commands when known Don’t use CommandBuilder.DeriveParameters()Don’t use CommandBuilder.DeriveParameters()
Specify Parameter information when knownSpecify Parameter information when known Don’t use MissingSchemaAction.AddWithKeyDon’t use MissingSchemaAction.AddWithKey
Specify Primary Key information when knownSpecify Primary Key information when known custTable.PrimaryKey = custTable.Columns("CustomerID")custTable.PrimaryKey = custTable.Columns("CustomerID") Don’t add Primary Key if not necessaryDon’t add Primary Key if not necessary
Necessary when Updating,Refreshing,Merging valuesNecessary when Updating,Refreshing,Merging values
Retrieving Multiple ResultsRetrieving Multiple Results Specify Batch statement or stored procedureSpecify Batch statement or stored procedure Use NextResult() to move to next set of resultsUse NextResult() to move to next set of results
Dim fMoreResults As Boolean = trueDim fMoreResults As Boolean = trueDim field As IntegerDim field As IntegerWhile(fMoreResults) While(fMoreResults)
For field = 0 To dr.FieldCount For field = 0 To dr.FieldCount Console.Write("/t"+dr.GetName(field)) Console.Write("/t"+dr.GetName(field))
NextNextConsole.WriteLine()Console.WriteLine()While(dr.Read())While(dr.Read())
For field = 0 to dr.FieldCountFor field = 0 to dr.FieldCountConsole.Write("/t"+dr(field))Console.Write("/t"+dr(field))
NextNextConsole.WriteLine()Console.WriteLine()
End WhileEnd WhilefMoreResults = dr.NextResult()fMoreResults = dr.NextResult()
End WhileEnd While
Populating Multiple DataTablesPopulating Multiple DataTables Retrieve multiple results in a single callRetrieve multiple results in a single call
Execute Batch statement or stored procdureExecute Batch statement or stored procdure Map results to appropriate tables using tablemappingsMap results to appropriate tables using tablemappings
Use ExecuteXmlReader to retrieve Use ExecuteXmlReader to retrieve hierarchical resultshierarchical results
Load with ReadXml using XmlReadMode.FragmentLoad with ReadXml using XmlReadMode.Fragment
Submit updates in batchesSubmit updates in batches Sample batch update example available soon…Sample batch update example available soon… Or save as DiffGram and send to SqlXmlOr save as DiffGram and send to SqlXml
Dim adapter As New SqlDataAdapter( _Dim adapter As New SqlDataAdapter( _"SELECT * FROM customers; SELECT * FROM orders",cnn)"SELECT * FROM customers; SELECT * FROM orders",cnn)
adapter.TableMappings.Add("Table1","Customer")adapter.TableMappings.Add("Table1","Customer")adapter.TableMappings.Add("Table2","Orders")adapter.TableMappings.Add("Table2","Orders")adapter.Fill(myDataSet)adapter.Fill(myDataSet)
Looking Up Values In The DatasetLooking Up Values In The Dataset Searching for Results within a DataSetSearching for Results within a DataSet
DataTable.Find() for searching on PK valuesDataTable.Find() for searching on PK values
DataView.Select() for repeated non-PK queriesDataView.Select() for repeated non-PK queries Sort DataView by search fieldsSort DataView by search fields DataView builds an index for sorted columnsDataView builds an index for sorted columns
Pass Table, filter, sort, RowState to constructorPass Table, filter, sort, RowState to constructor
Dim customer = customerTable.Rows.Find("GROSR")Dim customer = customerTable.Rows.Find("GROSR")
Dim customerView As New DataView(customerTable)Dim customerView As New DataView(customerTable)customerView.Sort = "State"customerView.Sort = "State"Dim customers = customerView.FindRows("CA")Dim customers = customerView.FindRows("CA")
Dim view As New DataView( Dim view As New DataView( __customerTable,customerTable, __"Country=USA","Country=USA", __"Region","Region", __
DataViewRowState.CurrentRows )DataViewRowState.CurrentRows )
Coding To Different ProvidersCoding To Different Providers
Use Activator to create root class Use Activator to create root class (DbConnection)(DbConnection)
Code to InterfacesCode to Interfaces Use CreateCommand() to get Use CreateCommand() to get
IDbCommandIDbCommand Take into account provider differencesTake into account provider differences
SqlClient named parameters versus OLE SqlClient named parameters versus OLE DB positional parametersDB positional parameters
CommandBuilder classesCommandBuilder classes
Provider Specific OptimizationsProvider Specific Optimizations
SqlClient .NET Data ProviderSqlClient .NET Data Provider Use CommandType.StoredProcedure to execute stored procsUse CommandType.StoredProcedure to execute stored procs
More efficient than executing command call{} or Exec syntaxMore efficient than executing command call{} or Exec syntax Set max connection lifetime for load balancingSet max connection lifetime for load balancing
Times out valid connections in order to balance across back-endsTimes out valid connections in order to balance across back-ends
OLE DB .NET Data ProviderOLE DB .NET Data Provider Use specific provider where availableUse specific provider where available
For example, the SqlClient .NET Data Provider…For example, the SqlClient .NET Data Provider… Specify type, size, precision, and scale of parametersSpecify type, size, precision, and scale of parameters
Otherwise we rebind w/each executeOtherwise we rebind w/each execute Connection.State is expensiveConnection.State is expensive
Round-trip to check stateRound-trip to check state Better to listen to change event to track stateBetter to listen to change event to track state
AgendaAgenda
Common TechniquesCommon Techniques Optimizing PerformanceOptimizing Performance Security ConsiderationsSecurity Considerations
Use Integrated SecurityUse Integrated Security Avoid String ConcatenationAvoid String Concatenation Use Stored ProceduresUse Stored Procedures Set Privileges AppropriatelySet Privileges Appropriately
QuestionsQuestions
Use Integrated SecurityUse Integrated Security
Don’t use sa account! (especially w/no Don’t use sa account! (especially w/no password)password)connect.ConnectionString = "server=localhost;uid=sa;password="connect.ConnectionString = "server=localhost;uid=sa;password="
Don’t embed password in connection stringDon’t embed password in connection stringconnect.ConnectionString = "server=localhost;uid=sa;password=pwd"connect.ConnectionString = "server=localhost;uid=sa;password=pwd"
Don’t concatenate UID/Password from user Don’t concatenate UID/Password from user into connection string (without validating into connection string (without validating input)input)connect.ConnectionString = "server=localhost;uid=sa;password="&pwdconnect.ConnectionString = "server=localhost;uid=sa;password="&pwd
pwd may be “pwd;Default Database = ‘master’”pwd may be “pwd;Default Database = ‘master’”
Use Integrated SecurityUse Integrated Securityconnect.ConnectionString = "server=localhost;Integrated Security=SSPI"connect.ConnectionString = "server=localhost;Integrated Security=SSPI"
Avoid String ConcatenationAvoid String Concatenation
Don’t concatenate user strings into command Don’t concatenate user strings into command texttext cmd.CommandText = "Select * from Customers where CustomerID = "&custIDcmd.CommandText = "Select * from Customers where CustomerID = "&custID
user may set custID = ‘“GROSR’; Drop Table user may set custID = ‘“GROSR’; Drop Table Customers;”Customers;”
InsteadInstead Have user select string from an enumeration, Have user select string from an enumeration,
rather than enter free textrather than enter free text Better yet; pass strings as Parameters:Better yet; pass strings as Parameters: cmd.CommandText = "Select * from Customers Where CustomerID = @CustID"cmd.CommandText = "Select * from Customers Where CustomerID = @CustID" cmd.Parameters.Add("@CustID",custID)cmd.Parameters.Add("@CustID",custID)
Use Stored ProceduresUse Stored Procedures
Controls what data user accesses Controls what data user accesses and howand how
Allows privileges to be set on Allows privileges to be set on Stored ProcedureStored Procedure
Can enforce additional business logicCan enforce additional business logic Added protection from malicious Added protection from malicious
string concatenationstring concatenation Values passed as parametersValues passed as parameters Validate strings passed to Stored Validate strings passed to Stored
ProcedureProcedure
Set Privileges AppropriatelySet Privileges Appropriately
Create user appropriate to client roleCreate user appropriate to client role Don’t just use “sa” for everything!Don’t just use “sa” for everything!
Set Privileges on resources accessedSet Privileges on resources accessed Stored ProceduresStored Procedures TablesTables ColumnsColumns
Additional InformationAdditional Information
Whitepapers:Whitepapers: ADO.NET for the ADO Programmer:ADO.NET for the ADO Programmer:
http://msdn.microsoft.com/library/en-us/dndotnet/html/ADONETProg.asphttp://msdn.microsoft.com/library/en-us/dndotnet/html/ADONETProg.asp
ADO.NET Best Practices (coming soon):ADO.NET Best Practices (coming soon):
http://msdn.microsoft.com/library/en-us/dndotnet/html/ADONETBest.asphttp://msdn.microsoft.com/library/en-us/dndotnet/html/ADONETBest.asp
ADO.NET On-Line Chat TranscriptADO.NET On-Line Chat Transcripthttp://msdn.microsoft.com/chats/vstudio/vstudio_012402.asphttp://msdn.microsoft.com/chats/vstudio/vstudio_012402.asp
The .NET Show: ADO.NET:The .NET Show: ADO.NET:http://msdn.microsoft.com/theshow/Episode017/default.asphttp://msdn.microsoft.com/theshow/Episode017/default.asp
Questions?Questions?
MSDN Architecture MSDN Architecture FastTrackFastTrack FastTrack sessions and reading listFastTrack sessions and reading list
http://www.mymsevents.com/http://www.mymsevents.com/MyMSEvents/Content.aspx?p=fast.htmMyMSEvents/Content.aspx?p=fast.htm
Comments on the FastTrack?Comments on the FastTrack? Please write on back of your review formPlease write on back of your review form
Contact [email protected] [email protected] Related content on Related content on
http://msdn.microsoft.com/BDAdotNEhttp://msdn.microsoft.com/BDAdotNETT
© 2002 Microsoft Corporation. All rights reserved.© 2002 Microsoft Corporation. All rights reserved.This presentation is for informational purposes only. Microsoft makes no warranties, express or implied, in this summary.This presentation is for informational purposes only. Microsoft makes no warranties, express or implied, in this summary.