Advanced dot net

872 views

Published on

Advanced dot net

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
872
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Advanced dot net

  1. 1. DAT402: Advanced ADO.NET Michael Pizzo Software Architect WebData Team Microsoft Corporation
  2. 3. Agenda <ul><li>Common Techniques </li></ul><ul><li>Optimizing Performance </li></ul><ul><li>Security Considerations </li></ul><ul><li>Questions </li></ul>
  3. 4. Agenda <ul><li>Common Techniques </li></ul><ul><ul><li>Updating from a DataSet </li></ul></ul><ul><ul><li>Server Cursors and ADO.NET </li></ul></ul><ul><ul><li>Using the DataSet as a Cache </li></ul></ul><ul><ul><li>Controlling how XML is generated </li></ul></ul><ul><ul><li>Working with Identity Columns </li></ul></ul><ul><ul><li>Handling Large ResultSets </li></ul></ul><ul><ul><li>Join Queries against a DataSet </li></ul></ul><ul><ul><li>Recordset  Dataset </li></ul></ul><ul><li>Optimizing Performance </li></ul><ul><li>Security Considerations </li></ul>
  4. 5. Updating From A DataSet <ul><li>Use a DataAdapter </li></ul><ul><ul><li>Typically one per table to be updated </li></ul></ul><ul><ul><li>Specify Insert, Update, Delete commands </li></ul></ul><ul><ul><ul><li>Use CommandBuilder for AdHoc queries </li></ul></ul></ul><ul><ul><ul><ul><li>Designers, tools </li></ul></ul></ul></ul><ul><ul><li>Call Update(), passing in DataSet/DataTable to be updated </li></ul></ul><ul><ul><ul><li>No persistent connection between DataAdapter and DataSet </li></ul></ul></ul><ul><ul><ul><ul><li>Can use different DataAdapters for Fill and Update </li></ul></ul></ul></ul><ul><ul><ul><ul><li>Use ExtendedProperties to pass datasource information </li></ul></ul></ul></ul>
  5. 6. Specifying Update Commands <ul><li>Associate parameters with DataSet Columns </li></ul><ul><ul><li>Specify SourceColumn property </li></ul></ul><ul><ul><li>Parameter values will be set using values from the Dataset for each Insert, Update, or Delete </li></ul></ul>'Specify Delete Command Dim delete As New SqlCommand(&quot;DeleteOrder&quot;, cnn) delete.CommandType = CommandType.StoredProcedure 'Create a Parameter and associate it with Dataset column Dim param As New SqlParameter(&quot;@OrderID&quot;, SqlDbType.Int) param.SourceColumn = &quot;OrderID&quot; delete.Parameters.Add(param) 'Set as the Delete Command adapter.DeleteCommand = delete
  6. 7. Updating Example Dim adapter As New SqlDataAdapter() Dim delete As New SqlCommand(&quot;DeleteOrder&quot;, cnn) delete.CommandType = CommandType.StoredProcedure delete.Parameters.Add(&quot;@OrderID&quot;, SqlDbType.Int).SourceColumn = &quot;OrderID&quot; adapter.DeleteCommand = delete Dim insert As New SqlCommand(&quot;AddOrder&quot;, cnn) insert.CommandType = CommandType.StoredProcedure insert.Parameters.Add(&quot;@OrderID&quot;, SqlDbType.Int).SourceColumn = &quot;OrderID&quot; insert.Parameters.Add(&quot;@CustD&quot;, SqlDbType.Int).SourceColumn = &quot;CustomerID&quot; insert.Parameters.Add(&quot;@Date&quot;, SqlDbType.DateTime).Value = DateTime.Now adapter.InsertCommand = insert Dim update As New SqlCommand(&quot;UpdateOrder&quot;, cnn) update.CommandType = CommandType.StoredProcedure update.Parameters.Add(&quot;@OrderID&quot;,SqlDbType.Int).SourceColumn=&quot;OrderID&quot; update.Parameters.Add(&quot;@CustD&quot;,SqlDbType.Int).SourceColumn=&quot;CustomerID&quot; adapter.UpdateCommand = update adapter.Update(ordersTable)
  7. 8. Optimistic Concurrency <ul><li>Specify Original values or RowVersion column </li></ul><ul><ul><li>Original values for changes made to data read </li></ul></ul><ul><ul><li>RowVersion for any changes made to row </li></ul></ul><ul><li>Set SourceVersion for original values </li></ul><ul><li>Account for Nulls in Where clause: </li></ul><ul><li>Add additional logic in Stored Procedure for handling concurrency and conflicts </li></ul>WHERE ((Country ISNULL) AND (@Country ISNULL)) OR (Country=@Country) Dim update As New SqlCommand(&quot;Update Customers (Name) Values &quot; & _ &quot;(@Name) Where Name=@OldName AND CustID=@ID&quot;,cnn) update.Parameters.Add(&quot;@Name&quot;,SqlDbType.VarChar).SourceColumn = &quot;Name&quot; update.Parameters.Add(&quot;@CustID&quot;,SqlDbType.Int).SourceColumn = &quot;CustomerID&quot; Dim param = update.Parameters.Add(&quot;@OldName&quot;,SqlDbType.VarChar) param.SourceColumn = &quot;Name&quot; param.SourceVersion = DataRowVersion.Original
  8. 9. Server Cursors And ADO.NET <ul><li>DataReader versus DataSet </li></ul><ul><ul><li>DataReader is a Forward Only/Read Only &quot;Server Cursor&quot; </li></ul></ul><ul><ul><li>Requests Data from Server as read on Client </li></ul></ul><ul><ul><ul><li>Buffered in packets at network level </li></ul></ul></ul><ul><ul><li>Holds state on the Server until closed </li></ul></ul><ul><li>Pessimistic Concurrency </li></ul><ul><ul><li>Locking records when read ensures updates don't fail due to concurrency violations </li></ul></ul><ul><ul><li>Kills scalability of application </li></ul></ul><ul><ul><li>Supported in ADO.NET through transactions </li></ul></ul><ul><li>Scrollable Server Cursors </li></ul><ul><ul><li>Holds State on Server </li></ul></ul><ul><ul><li>Round-trip per fetch/update </li></ul></ul><ul><ul><li>Generally better handled in DataSet or Stored Procedure </li></ul></ul><ul><ul><li>Supported in ADO.NET through Cursor DML commands </li></ul></ul>
  9. 10. Pessimistic Concurrency 'Start a transaction and set on Select command Dim connect = adapter.SelectCommand.Connection connect.Open() Dim tran = connect.BeginTransaction(IsolationLevel.Serializable) adapter.SelectCommand.Transaction = tran 'Fill DataSet and make changes adapter.Fill(ds, &quot;Employees&quot;) Dim employee As DataRow For Each employee In ds.Tables(&quot;Employees&quot;).Rows employee(&quot;Salary&quot;) = employee(&quot;Salary&quot;) * 1.1 Next 'Set the connection and transaction for the update command adapter.UpdateCommand.Connection = connect adapter.UpdateCommand.Transaction = tran adapter.Update(ds,&quot;Employees&quot;) tran.Commit() connect.Close()
  10. 11. Scrollable Server Cursors ' Declare and open cursor Dim cmd As New SqlCommand(Nothing, conn) cmd.CommandText = “DECLARE mycursor SCROLLABLE CURSOR FOR select * from Customers” cmd.ExecuteNonQuery() cmd.CommandText = “OPEN mycursor” cmd.ExecuteNonQuery() ' Read from cursor Dim dr As SqlDataReader cmd.CommandText = “FETCH NEXT FROM mycursor” While(True) dr =cmd.ExecuteReader() if (dr.Read() = false) Then Exit While Console.WriteLine(&quot;CompanyName is &quot; & dr(&quot;CompanyName&quot;)) dr.Close() End While ' Update fifth row cmd.CommandText = “FETCH ABSOLUTE 5 FROM mycursor” cmd.ExecuteNonQuery() cmd.CommandText = “UPDATE Customers set FirstName = ‘Bill’ WHERE CURRENT OF mycursor” cmd.ExecuteNonQuery() ' Close the cursor cmd.CommandText = “CLOSE mycursor; DEALLOCATE mycursor” cmd.ExecuteNonQuery()
  11. 12. Using The Dataset As A Cache <ul><li>DataSet optimized for multi-threaded read access </li></ul><ul><ul><li>Can put in ASP.NET cache </li></ul></ul><ul><ul><li>Doesn't take locks </li></ul></ul><ul><ul><li>Need to synchronize writes </li></ul></ul><ul><ul><li>Clone, Update, and replace </li></ul></ul>Function GetCategories() As DataSet 'See if DataSet exists in Cache Dim categories As DataSet = Cache(&quot;CategoriesDS&quot;) if (categories Is Nothing) ' not in cache 'Create DataAdapter and Load DataSet Dim adapter As new SqlDataAdapter( _ &quot;Select CategoryName from Categories&quot;,cnn) adapter.Fill(categories) 'Put Categories Dataset into Cache Cache(&quot;CategoriesDS&quot;)=categories End If return categories End Function
  12. 13. Controlling How The XML Is Generated <ul><li>DataSet lets you control how XML is generated </li></ul><ul><li>Name, Namespace properties on DataSet, DataTable, DataColumn </li></ul><ul><li>MappingType property on DataColumn defines how data is written </li></ul><ul><ul><li>Element, Attribute, SimpleType, Hidden </li></ul></ul><ul><li>Nested Property on DataRelation controls how children are written </li></ul>' Write out CustomerID, OrderID as Attributes ds.Tables(&quot;Customers&quot;).Columns(&quot;CustomerID&quot;).ColumnMapping = MappingType.Attribute ds.Tables(&quot;Orders&quot;).Columns(&quot;OrderID&quot;).ColumnMapping = MappingType.Attribute ' Write out Orders as children of Customers ds.Relations(&quot;cust_orders&quot;).Nested = True <?xml version=&quot;1.0&quot; standalone=&quot;yes&quot;?> <CustomerOrders> <Customers CustomerID=&quot;GROSR&quot;> <ContactName>Manuel Pereira</ContactName> <Orders OrderID=&quot;10268&quot;> <CustomerID>GROSR</CustomerID> <OrderDate>1996-07-30</OrderDate> </Orders> </Customers> </CustomerOrders>
  13. 14. Working With Identity Columns <ul><li>Issue: Primary Keys must be unique across consumers in order to merge back to database </li></ul><ul><li>Solution #1: Use a GUID as the Primary Key where possible </li></ul><ul><ul><li>Can be generated on the Client or Server </li></ul></ul><ul><ul><ul><li>Guaranteed to be unique </li></ul></ul></ul><ul><ul><li>Doesn’t change when updated to the Server </li></ul></ul><ul><ul><ul><li>No fixup required for child rows </li></ul></ul></ul>
  14. 15. <ul><li>Issue: Primary Keys must be unique across consumers in order to merge back to database </li></ul><ul><li>Solution #2: If you are stuck with an AutoIncrement Primary Key… </li></ul><ul><ul><li>First, make sure Client value doesn't conflict with Server value </li></ul></ul><ul><ul><ul><li>Set AutoIncrement Seed, Step to -1 on DataSet </li></ul></ul></ul><ul><ul><li>To update values in Dataset, select back the ID in your InsertCommand </li></ul></ul><ul><ul><ul><li>Update the inserted row with the new value </li></ul></ul></ul><ul><ul><li>Insert Parent rows before Child Rows </li></ul></ul><ul><ul><ul><li>Use UpdateRule.Cascade </li></ul></ul></ul><ul><ul><li>If you are merging with the original DataSet, prevent AcceptChanges from being called on Inserted row </li></ul></ul><ul><ul><ul><li>Specify SkipCurrentRow in OnRowUpdated Eventhandler </li></ul></ul></ul>Working With Identity Columns
  15. 16. Inserting AutoIncrement Values Sub UpdateData(table As DataTable) ' Set InsertCommand with returned Identity Dim insert As New SqlCommand( _ &quot;Insert into Orders(OrderID,Date) values @OrderID, @Date)&quot; _ & &quot;;Select SCOPE_IDENTITY() as OrderID &quot;,cnn) insert.Parameters.Add(&quot;@OrderID&quot;,SqlDbType.Int).SourceColumn=&quot;OrderID&quot; insert.Parameters.Add(&quot;@Date&quot;,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 AcceptChanges e.Status = UpdateStatus.SkipCurrentRow End Sub
  16. 17. Refreshing Data In The DataSet <ul><li>To update DataSet with current values from the Database values </li></ul><ul><ul><li>Specify a Primary Key on the Table </li></ul></ul><ul><ul><li>Use Adapter.Fill() </li></ul></ul><ul><ul><ul><li>Fill will update existing values if you have a Primary key </li></ul></ul></ul><ul><li>To update original values but preserve changes </li></ul><ul><ul><li>Fill a new DataSet and use DataSet.Merge() with PreserveChanges=true </li></ul></ul>
  17. 18. Handling Large ResultSets <ul><li>Select just the data the user needs </li></ul><ul><ul><li>User rarely wants to scroll through >100 records </li></ul></ul><ul><li>Call Cancel() on command to dispose of data beyond what is read </li></ul><ul><li>Page through large results </li></ul><ul><ul><li>Use TOP n in SQL Server </li></ul></ul><ul><ul><li>Use MaxRows for other databases </li></ul></ul><ul><ul><li>Paging by Ordinal range (i.e., records 91-100) </li></ul></ul><ul><ul><li>Paging by value range (i.e., M-N) </li></ul></ul><ul><ul><ul><li>Parameterized Where clause </li></ul></ul></ul>SELECT TOP 10 * FROM (SELECT TOP 100 ProductName, UnitPrice FROM Products ORDER BY UnitPrice ASC) AS Products ORDER BY UnitPrice DESC SELECT TOP 10 ProductName, UnitPrice FROM Products WHERE ProductName > @PreviousName ORDER BY ProductName ASC AS Products
  18. 19. Join Queries Against DataSet <ul><li>Use Relations to navigate from parent to child </li></ul><ul><ul><li>Restriction: Can’t limit parent based on child values </li></ul></ul><ul><li>Use XmlDataDocument </li></ul><ul><ul><li>X/Path queries can be hierarchical </li></ul></ul><ul><ul><li>Can get DataRows corresponding to returned Elements </li></ul></ul>Dim nodeslist = xmlData.SelectNodes(&quot;//Customers/Orders[@State=WA]&quot;) Dim customer, order As DataRow For Each customer in CustomerTable.Select(&quot;State='WA'&quot;) Console.WriteLine(&quot;Customer: &quot; & customer(&quot;ContactName&quot;)) For Each order in customer.GetChildRows(&quot;custord&quot;) Console.WriteLine(&quot;Order Amount = &quot; & order(&quot;Amount&quot;)) Next Next Dim node As XmlNode Dim customer as DataRow For Each node in nodelist customer = xmlData.GetRowFromElement(node) Next
  19. 20. RecordSet  DataSet <ul><li>Getting Data From a RecordSet </li></ul><ul><ul><li>Use the OleDbDataAdapter.Fill() method </li></ul></ul><ul><ul><li>Persist the RecordSet as XML and load into the DataSet </li></ul></ul><ul><ul><ul><li>Use an XSL/T for best results </li></ul></ul></ul><ul><li>Getting Data From a DataSet to a RecordSet </li></ul><ul><ul><li>Write an XSL Transform to do the conversion </li></ul></ul><ul><ul><ul><li>We hope to provide one of these in the future </li></ul></ul></ul><ul><ul><li>Walk through the DataSet and generate the XDR description then write out the XML in attribute-centric format </li></ul></ul><ul><ul><ul><li>Sample coming soon… </li></ul></ul></ul><ul><ul><li>Dim rs As New ADODB.Recordset() </li></ul></ul><ul><ul><li>rs.Open(&quot;Select * from Orders&quot;, &quot;Data Source=localhost;Integrated Security=true&quot;) </li></ul></ul><ul><ul><li>Dim adapter As New OleDbDataAdapter() </li></ul></ul><ul><ul><li>adapter.Fill(ds, rs, &quot;Orders&quot;) </li></ul></ul><ul><ul><li>Dim ds As New DataSet() </li></ul></ul><ul><ul><li>ds.ReadXml(&quot;rsOrders.xml) </li></ul></ul>
  20. 21. Agenda <ul><li>Common Techniques </li></ul><ul><li>Optimizing Performance </li></ul><ul><ul><li>Optimizing Data Retrieval </li></ul></ul><ul><ul><li>Using Schema at Design Time </li></ul></ul><ul><ul><li>Using Batches </li></ul></ul><ul><ul><li>Looking Up Values in the DataSet </li></ul></ul><ul><ul><li>Provider specific Optimizations </li></ul></ul><ul><li>Security Considerations </li></ul><ul><li>Questions </li></ul>
  21. 22. Optimizing Data Retrieval <ul><li>Use ExecuteNonQuery if no results to be returned </li></ul><ul><ul><li>DDL commands,Inserts, Updates, Deletes </li></ul></ul><ul><ul><li>Non-result returning Stored Procedures (parameters ok) </li></ul></ul><ul><li>Return single set of values using Output Parameters </li></ul><ul><ul><li>Dim numRowsAffected As Integer = cmd.ExecuteNonQuery() </li></ul></ul><ul><ul><li>cmd.CommandText = _ </li></ul></ul><ul><ul><li>&quot;Select @ContactName = ContactName From Customers where CustomerID = 'GROSR'&quot; </li></ul></ul><ul><ul><li>Dim param = cmd.Parameters.Add(&quot;@ContactName&quot;,SqlDbType.VarChar,25) </li></ul></ul><ul><ul><li>param.Direction = ParameterDirection.Output </li></ul></ul><ul><ul><li>cmd.ExecuteNonQuery() </li></ul></ul><ul><ul><li>Console.WriteLine(&quot;Contact Name = &quot;& param.Value) </li></ul></ul>
  22. 23. Efficiently Retrieving BLOBs <ul><li>Use CommandBehavior.SequentialAccess </li></ul><ul><ul><li>Must retrieve columns in order </li></ul></ul><ul><ul><li>No buffering of column values </li></ul></ul><ul><ul><li>Dim cmd As New SqlCommand( &quot;Select * from Employees&quot;, cnn) </li></ul></ul><ul><ul><li>Dim results = cmd.ExecuteReader(CommandBehavior.SequentialAccess) </li></ul></ul><ul><ul><li>Dim i As Integer </li></ul></ul><ul><ul><li>While(results.Read()) </li></ul></ul><ul><ul><li>For i=0 to results.FieldCount </li></ul></ul><ul><ul><li>Console.Write(&quot; &quot;& results(i)) </li></ul></ul><ul><ul><li>Console.WriteLine() </li></ul></ul><ul><ul><li>Next </li></ul></ul><ul><ul><li>End While </li></ul></ul>
  23. 24. Use Metadata At Design Time <ul><li>DataReader </li></ul><ul><ul><li>Strongly Typed Ordinal accessors </li></ul></ul><ul><ul><li>Dim name As String = dr.GetString(0) </li></ul></ul><ul><li>DataSet </li></ul><ul><ul><li>Load, don't infer, schema </li></ul></ul><ul><li>Data Adapter </li></ul><ul><ul><li>Don’t use CommandBuilder </li></ul></ul><ul><ul><ul><li>Specify insert,update,delete commands when known </li></ul></ul></ul><ul><ul><li>Don’t use CommandBuilder.DeriveParameters() </li></ul></ul><ul><ul><ul><li>Specify Parameter information when known </li></ul></ul></ul><ul><ul><li>Don’t use MissingSchemaAction.AddWithKey </li></ul></ul><ul><ul><ul><li>Specify Primary Key information when known </li></ul></ul></ul><ul><ul><li>custTable.PrimaryKey = custTable.Columns(&quot;CustomerID&quot;) </li></ul></ul><ul><ul><li>Don’t add Primary Key if not necessary </li></ul></ul><ul><ul><ul><li>Necessary when Updating,Refreshing,Merging values </li></ul></ul></ul>
  24. 25. Retrieving Multiple Results <ul><li>Specify Batch statement or stored procedure </li></ul><ul><li>Use NextResult() to move to next set of results </li></ul><ul><ul><li>Dim fMoreResults As Boolean = true </li></ul></ul><ul><ul><li>Dim field As Integer </li></ul></ul><ul><ul><li>While(fMoreResults) </li></ul></ul><ul><ul><li>For field = 0 To dr.FieldCount </li></ul></ul><ul><ul><li>Console.Write(&quot;/t&quot;+dr.GetName(field)) </li></ul></ul><ul><ul><li>Next </li></ul></ul><ul><ul><li>Console.WriteLine() </li></ul></ul><ul><ul><li>While(dr.Read()) </li></ul></ul><ul><ul><li>For field = 0 to dr.FieldCount </li></ul></ul><ul><ul><li>Console.Write(&quot;/t&quot;+dr(field)) </li></ul></ul><ul><ul><li>Next </li></ul></ul><ul><ul><li>Console.WriteLine() </li></ul></ul><ul><ul><li>End While </li></ul></ul><ul><ul><li>fMoreResults = dr.NextResult() </li></ul></ul><ul><ul><li>End While </li></ul></ul>
  25. 26. Populating Multiple DataTables <ul><li>Retrieve multiple results in a single call </li></ul><ul><ul><li>Execute Batch statement or stored procdure </li></ul></ul><ul><ul><ul><li>Map results to appropriate tables using tablemappings </li></ul></ul></ul><ul><li>Use ExecuteXmlReader to retrieve hierarchical results </li></ul><ul><ul><ul><li>Load with ReadXml using XmlReadMode.Fragment </li></ul></ul></ul><ul><li>Submit updates in batches </li></ul><ul><ul><li>Sample batch update example available soon… </li></ul></ul><ul><ul><li>Or save as DiffGram and send to SqlXml </li></ul></ul><ul><ul><li>Dim adapter As New SqlDataAdapter( _ </li></ul></ul><ul><ul><li>&quot;SELECT * FROM customers; SELECT * FROM orders&quot;,cnn) </li></ul></ul><ul><ul><li>adapter.TableMappings.Add(&quot;Table1&quot;,&quot;Customer&quot;) </li></ul></ul><ul><ul><li>adapter.TableMappings.Add(&quot;Table2&quot;,&quot;Orders&quot;) </li></ul></ul><ul><ul><li>adapter.Fill(myDataSet) </li></ul></ul>
  26. 27. Looking Up Values In The Dataset <ul><li>Searching for Results within a DataSet </li></ul><ul><ul><li>DataTable.Find() for searching on PK values </li></ul></ul><ul><ul><li>DataView.Select() for repeated non-PK queries </li></ul></ul><ul><ul><ul><li>Sort DataView by search fields </li></ul></ul></ul><ul><ul><ul><li>DataView builds an index for sorted columns </li></ul></ul></ul><ul><li>Pass Table, filter, sort, RowState to constructor </li></ul><ul><ul><li>Dim customer = customerTable.Rows.Find(&quot;GROSR&quot;) </li></ul></ul><ul><ul><li>Dim customerView As New DataView(customerTable) </li></ul></ul><ul><ul><li>customerView.Sort = &quot;State&quot; </li></ul></ul><ul><ul><li>Dim customers = customerView.FindRows(&quot;CA&quot;) </li></ul></ul><ul><ul><li>Dim view As New DataView( _ </li></ul></ul><ul><ul><li>customerTable, _ </li></ul></ul><ul><ul><li>&quot;Country=USA&quot;, _ </li></ul></ul><ul><ul><li>&quot;Region&quot;, _ DataViewRowState.CurrentRows ) </li></ul></ul>
  27. 28. Coding To Different Providers <ul><li>Use Activator to create root class (DbConnection) </li></ul><ul><li>Code to Interfaces </li></ul><ul><li>Use CreateCommand() to get IDbCommand </li></ul><ul><li>Take into account provider differences </li></ul><ul><ul><li>SqlClient named parameters versus OLE DB positional parameters </li></ul></ul><ul><ul><li>CommandBuilder classes </li></ul></ul>
  28. 29. Provider Specific Optimizations <ul><li>SqlClient .NET Data Provider </li></ul><ul><ul><li>Use CommandType.StoredProcedure to execute stored procs </li></ul></ul><ul><ul><ul><li>More efficient than executing command call{} or Exec syntax </li></ul></ul></ul><ul><ul><li>Set max connection lifetime for load balancing </li></ul></ul><ul><ul><ul><li>Times out valid connections in order to balance across back-ends </li></ul></ul></ul><ul><li>OLE DB .NET Data Provider </li></ul><ul><ul><li>Use specific provider where available </li></ul></ul><ul><ul><ul><li>For example, the SqlClient .NET Data Provider… </li></ul></ul></ul><ul><ul><li>Specify type, size, precision, and scale of parameters </li></ul></ul><ul><ul><ul><li>Otherwise we rebind w/each execute </li></ul></ul></ul><ul><ul><li>Connection.State is expensive </li></ul></ul><ul><ul><ul><li>Round-trip to check state </li></ul></ul></ul><ul><ul><ul><li>Better to listen to change event to track state </li></ul></ul></ul>
  29. 30. Agenda <ul><li>Common Techniques </li></ul><ul><li>Optimizing Performance </li></ul><ul><li>Security Considerations </li></ul><ul><ul><li>Use Integrated Security </li></ul></ul><ul><ul><li>Avoid String Concatenation </li></ul></ul><ul><ul><li>Use Stored Procedures </li></ul></ul><ul><ul><li>Set Privileges Appropriately </li></ul></ul><ul><li>Questions </li></ul>
  30. 31. Use Integrated Security <ul><li>Don’t use sa account! (especially w/no password) </li></ul><ul><ul><li>connect.ConnectionString = &quot;server=localhost;uid=sa;password=&quot; </li></ul></ul><ul><li>Don’t embed password in connection string </li></ul><ul><ul><li>connect.ConnectionString = &quot;server=localhost;uid=sa;password=pwd&quot; </li></ul></ul><ul><li>Don’t concatenate UID/Password from user into connection string (without validating input) </li></ul><ul><ul><li>connect.ConnectionString = &quot;server=localhost;uid=sa;password=&quot;&pwd </li></ul></ul><ul><ul><li>pwd may be “pwd;Default Database = ‘master’” </li></ul></ul><ul><li>Use Integrated Security </li></ul><ul><ul><li>connect.ConnectionString = &quot;server=localhost;Integrated Security=SSPI&quot; </li></ul></ul>
  31. 32. Avoid String Concatenation <ul><li>Don’t concatenate user strings into command text </li></ul><ul><ul><li>cmd.CommandText = &quot;Select * from Customers where CustomerID = &quot;&custID </li></ul></ul><ul><ul><li>user may set custID = ‘“GROSR’; Drop Table Customers;” </li></ul></ul><ul><li>Instead </li></ul><ul><ul><li>Have user select string from an enumeration, rather than enter free text </li></ul></ul><ul><ul><li>Better yet; pass strings as Parameters: </li></ul></ul><ul><ul><li>cmd.CommandText = &quot;Select * from Customers Where CustomerID = @CustID&quot; </li></ul></ul><ul><ul><li>cmd.Parameters.Add(&quot;@CustID&quot;,custID) </li></ul></ul>
  32. 33. Use Stored Procedures <ul><li>Controls what data user accesses and how </li></ul><ul><li>Allows privileges to be set on Stored Procedure </li></ul><ul><li>Can enforce additional business logic </li></ul><ul><li>Added protection from malicious string concatenation </li></ul><ul><ul><li>Values passed as parameters </li></ul></ul><ul><ul><li>Validate strings passed to Stored Procedure </li></ul></ul>
  33. 34. Set Privileges Appropriately <ul><li>Create user appropriate to client role </li></ul><ul><ul><li>Don’t just use “sa” for everything! </li></ul></ul><ul><li>Set Privileges on resources accessed </li></ul><ul><ul><li>Stored Procedures </li></ul></ul><ul><ul><li>Tables </li></ul></ul><ul><ul><li>Columns </li></ul></ul>
  34. 35. Additional Information <ul><li>Whitepapers: </li></ul><ul><ul><li>ADO.NET for the ADO Programmer: </li></ul></ul><ul><li>http://msdn.microsoft.com/library/en-us/dndotnet/html/ADONETProg.asp </li></ul><ul><ul><li>ADO.NET Best Practices (coming soon): </li></ul></ul><ul><li>http://msdn.microsoft.com/library/en-us/dndotnet/html/ADONETBest.asp </li></ul><ul><li>ADO.NET On-Line Chat Transcript </li></ul><ul><li>http://msdn.microsoft.com/chats/vstudio/vstudio_012402.asp </li></ul><ul><li>The .NET Show: ADO.NET: </li></ul><ul><li>http://msdn.microsoft.com/theshow/Episode017/default.asp </li></ul>
  35. 36. Questions?
  36. 37. MSDN Architecture FastTrack <ul><li>FastTrack sessions and reading list </li></ul><ul><ul><li>http://www.mymsevents.com/MyMSEvents/Content.aspx?p=fast.htm </li></ul></ul><ul><li>Comments on the FastTrack? </li></ul><ul><ul><li>Please write on back of your review form </li></ul></ul><ul><li>Contact bda.net@microsoft.com </li></ul><ul><li>Related content on http://msdn.microsoft.com/BDAdotNET </li></ul>
  37. 38. © 2002 Microsoft Corporation. All rights reserved. This presentation is for informational purposes only. Microsoft makes no warranties, express or implied, in this summary.

×