ado.net tips and tricks how to get the most out of your data access…

34
ADO.NET Tips and Tricks How to get the most out of your data access…

Upload: cory-morris

Post on 26-Dec-2015

224 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: ADO.NET Tips and Tricks How to get the most out of your data access…

ADO.NET Tips and Tricks

ADO.NET Tips and Tricks

How to get the most out of your data access…

How to get the most out of your data access…

Page 2: ADO.NET Tips and Tricks How to get the most out of your data access…

Who I AmWho I Am

Shawn Wildermuth ([email protected]) Independent Consultant (http://adoguy.com) C# MVP MCSD.NET INETA Speaker Book Author – “Pragmatic ADO.NET”

Editor of http://ONDotnet.com This Presentation can be found at:– http://adoguy.com/presentations

Shawn Wildermuth ([email protected]) Independent Consultant (http://adoguy.com) C# MVP MCSD.NET INETA Speaker Book Author – “Pragmatic ADO.NET”

Editor of http://ONDotnet.com This Presentation can be found at:– http://adoguy.com/presentations

Page 3: ADO.NET Tips and Tricks How to get the most out of your data access…

OverviewOverview

Can you do ADO.NET Wrong? Unmanaged Resources Connections DataSets Typed DataSets CommandBuilders DataAdapters DataSets & ASP.NET Distributed DataSets Transactions Vendor Neutral Code

Can you do ADO.NET Wrong? Unmanaged Resources Connections DataSets Typed DataSets CommandBuilders DataAdapters DataSets & ASP.NET Distributed DataSets Transactions Vendor Neutral Code

Page 4: ADO.NET Tips and Tricks How to get the most out of your data access…

Are You Doing Anything Wrong?Are You Doing Anything Wrong?

Can you write bad ADO.NET Code? Yes…but:– It is usually intuitive about what is right– Disconnected nature makes it harder to break

Areas of confusion about ADO.NET– Easier to scale, but not fool-proof– Disconnected does not mean scalable– Concurrency is harder than older systems

(e.g. ADO)

– “Why Can’t I Lock Rows”

Can you write bad ADO.NET Code? Yes…but:– It is usually intuitive about what is right– Disconnected nature makes it harder to break

Areas of confusion about ADO.NET– Easier to scale, but not fool-proof– Disconnected does not mean scalable– Concurrency is harder than older systems

(e.g. ADO)

– “Why Can’t I Lock Rows”

Page 5: ADO.NET Tips and Tricks How to get the most out of your data access…

Unmanaged ResourcesUnmanaged Resources

Most ADO.NET objects are IDisposable– Make sure and Dispose()– Inheritance from Component means

Dispose()– Not disposing objects will cause leaky

code

Most ADO.NET objects are IDisposable– Make sure and Dispose()– Inheritance from Component means

Dispose()– Not disposing objects will cause leaky

codeDim conn As New SqlConnection("...")Dim cmd As SqlCommand = conn.CreateCommand()cmd.CommandText = "..."

conn.Open()Dim rdr As SqlDataReader = cmd.ExecuteReader()While rdr.Read() Console.WriteLine(rdr.GetString(0))End Whileconn.Close()

' potential leaky code even though you closed the connection!

Page 6: ADO.NET Tips and Tricks How to get the most out of your data access…

Unmanaged Resources (3)Unmanaged Resources (3)

Use Try…Finally– No “using” in VB.NET, so must do it all in

finally– Ensures always disposed, even if

exception

Use Try…Finally– No “using” in VB.NET, so must do it all in

finally– Ensures always disposed, even if

exception

Dim conn as new SqlConnection("...")Dim cmd as SqlCommand = conn.CreateConnectionTry cmd.CommandText = "..." conn.Open()

Dim rdr as SqlDataReader = cmd.ExecuteReader() While rdr.Read() = True Console.WriteLine(rdr.GetString(0)) End While

Finally ' Clean Up conn.Close() rdr.Dispose() cmd.Dispose() conn.Dispose()End Try

Page 7: ADO.NET Tips and Tricks How to get the most out of your data access…

ConnectionsConnections

Connections are precious– In DataReaders, let it close it when it is

done:

Connections are precious– In DataReaders, let it close it when it is

done:Try conn.Open()

Dim rdr As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection)

While rdr.Read() ' Closes Here on last read Console.WriteLine(rdr.GetString(0)) End While

Finally ' Make sure we didn’t throw an exception and leak a connection conn.Close()End Try

Page 8: ADO.NET Tips and Tricks How to get the most out of your data access…

Connections (2)Connections (2)

Connections are still precious– With DataSets, never open them:

Connections are still precious– With DataSets, never open them:

Dim ds As New DataSetDim conn As New SqlConnection("...")Dim adapter As New SqlDataAdapter("...")

' The adapter will always preserve the state ' of the connection during the fill

' No Need to Open Or Close the connection because the adapter will ' close it as soon as possible since it is already closedadapter.Fill(ds)

' If it is open, it will leave it openconn.Open()adapter.Fill(ds)conn.Close()

Page 9: ADO.NET Tips and Tricks How to get the most out of your data access…

Connections (3)Connections (3)

Don’t Hardcode Connection Strings– VS.NET Designer Does it Wrong– Store Them in .Config Files

Don’t Hardcode Connection Strings– VS.NET Designer Does it Wrong– Store Them in .Config Files<configuration> <appSettings> <add key="SqlConnString" value="Server=localhost;Database=Northwind;..." /> </appSettings></configuration>

<configuration> <appSettings> <!-- Better yet, encrypt them in the .config file --> <add key="SqlConnString" value="E234998F98A98F..." /> </appSettings></configuration>

Page 10: ADO.NET Tips and Tricks How to get the most out of your data access…

Connections (4)Connections (4)

Use Connection Factories– Isolates Connection String Storage– Increases Security by Controlling Access

Use Connection Factories– Isolates Connection String Storage– Increases Security by Controlling Access

Public Class ConnectionStrings Public Shared Function GetConnection() _ As OleDbConnection

return new OleDbConnection( _ ConfigurationSettings.AppSettings("ConnString"))

End FunctionEnd Class

Page 11: ADO.NET Tips and Tricks How to get the most out of your data access…

Connections (5)Connections (5)

Secure Connection Strings with Encryption– You could write your own Encryption Library– Better would be if MS supplied one

In Windows 2000 and later, use DPAPI DPAPI Eliminates the Key Management Problem Encrypts on a Per Machine or Per User Basis Caveat: May Require Encrypting on Every Machine

Described in Detail in a Microsoft PAP:– http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT07.asp

Secure Connection Strings with Encryption– You could write your own Encryption Library– Better would be if MS supplied one

In Windows 2000 and later, use DPAPI DPAPI Eliminates the Key Management Problem Encrypts on a Per Machine or Per User Basis Caveat: May Require Encrypting on Every Machine

Described in Detail in a Microsoft PAP:– http://msdn.microsoft.com/library/en-us/dnnetsec/html/SecNetHT07.asp

Page 12: ADO.NET Tips and Tricks How to get the most out of your data access…

DataSetsDataSets

DataSets have full database schema– Tables– Columns– Primary and Foreign Keys– Constraints– Even Triggers

Using Full Schema– Allows validation without a network roundtrip– Relationships allows hierarchical view of data

DataSets have full database schema– Tables– Columns– Primary and Foreign Keys– Constraints– Even Triggers

Using Full Schema– Allows validation without a network roundtrip– Relationships allows hierarchical view of data

Page 13: ADO.NET Tips and Tricks How to get the most out of your data access…

DataSets (2)DataSets (2)

Use DataSets for data caching– Most data is not volatile– Saves roundtrips to the database– Less code than writing custom classes

Use DataSets for data caching– Most data is not volatile– Saves roundtrips to the database– Less code than writing custom classes

Public Class _default Inherits Page Dim _dataSet As DataSet Const MYDATASET As String = "MYDATASET" Sub OnLoad(...) If Session(MYDATASET) Is Nothing Then _dataSet = New DataSet Else _dataSet = DirectCast(Session(MYDATASET), DataSet) End If '... Session(MYDATASET) = _dataSet

End SubEnd Class

Page 14: ADO.NET Tips and Tricks How to get the most out of your data access…

DataSets (3)DataSets (3)

DataSets can incrementally grow– DataAdapter.Fill adds to DataSet, not

replace– Allows cache to be built up as needed

DataSets can incrementally grow– DataAdapter.Fill adds to DataSet, not

replace– Allows cache to be built up as needed' Fill DataSet with Product #1adapter.SelectCommand.Parameters("@product").Value = 1adapter.Fill(_dataSet)

'...

' Fill DataSet with Product #2adapter.SelectCommand.Parameters("@product").Value = 2adapter.Fill(_dataSet)

' Now DataSet has both products in, not just one or the other

Page 15: ADO.NET Tips and Tricks How to get the most out of your data access…

DataSets (4)DataSets (4)

Use DataSets for intermittent connectivity– Store locally between times of

connectivity– Stored DataSets preserve state of

changes– Great for mobile apps (not just CF)

Use DataSets for intermittent connectivity– Store locally between times of

connectivity– Stored DataSets preserve state of

changes– Great for mobile apps (not just CF)

Public Class MainForm Inherits Form Dim _dataSet as DataSet

Sub OnClose(...) _dataSet.WriteXml("foo.xml", XmlWriteMode.DiffGram) End Sub

Sub OnLoad(...) _dataSet.ReadXml("foo.xml", XmlReadMode.DiffGram) End SubEnd Class

Page 16: ADO.NET Tips and Tricks How to get the most out of your data access…

Typed DataSetsTyped DataSets

Use Typed DataSets for Stable Schemas– Data Validate at Compile Time– Improves Developer Productivity with

Intellisense– Easier to maintain in light of schema

changes than code– Loads XSD Schema in with Code (faster)– Demo

Use Typed DataSets for Stable Schemas– Data Validate at Compile Time– Improves Developer Productivity with

Intellisense– Easier to maintain in light of schema

changes than code– Loads XSD Schema in with Code (faster)– Demo

Page 17: ADO.NET Tips and Tricks How to get the most out of your data access…

CommandBuildersCommandBuilders

Good for Simple Optimistic Concurrency– Concurrency based on original values– Robust, but Inefficient– Update and Deletes are huge– No support for Stored Procedures

Good for Simple Optimistic Concurrency– Concurrency based on original values– Robust, but Inefficient– Update and Deletes are huge– No support for Stored ProceduresDELETE FROM CUSTOMER WHERE ( (CustomerID = @p3) AND ((FirstName IS NULL AND @p4 IS NULL) OR (FirstName = @p5)) AND ((LastName IS NULL AND @p6 IS NULL) OR (LastName = @p7)) AND ((MiddleName IS NULL AND @p8 IS NULL) OR (MiddleName = @p9)) AND ((Address IS NULL AND @p10 IS NULL) OR (Address = @p11)) AND ((Apartment IS NULL AND @p12 IS NULL) OR (Apartment = @p13)) AND ((City IS NULL AND @p14 IS NULL) OR (City = @p15)) AND ((State IS NULL AND @p16 IS NULL) OR (State = @p17)) AND ((Zip IS NULL AND @p18 IS NULL) OR (Zip = @p19)) AND ((HomePhone IS NULL AND @p20 IS NULL) OR (HomePhone = @p21)) AND ((BusinessPhone IS NULL AND @p22 IS NULL) OR (BusinessPhone = @p23)) AND ((DOB IS NULL AND @p24 IS NULL) OR (DOB = @p25)) AND ((Discount IS NULL AND @p26 IS NULL) OR (Discount = @p27)) AND ((CheckedOut IS NULL AND @p28 IS NULL) OR (CheckedOut = @p29)) )

Page 18: ADO.NET Tips and Tricks How to get the most out of your data access…

CommandBuilders (2)CommandBuilders (2)

Conclusion– Great for Prototyping– Bad for Production Code– Designer can achieve same at compile

time

Conclusion– Great for Prototyping– Bad for Production Code– Designer can achieve same at compile

time

Page 19: ADO.NET Tips and Tricks How to get the most out of your data access…

DataAdaptersDataAdapters

Use different adapters to load and update– Batch Adapters to load multiple tables– Single table adapters to update tables

Use Designer Support– Add a Component to project to hold Adapters– Use designer to create Mappings– Get CommandBuilder Behavior at Compile-

time– Or Map to Stored Procedures (preferred)

Demo

Use different adapters to load and update– Batch Adapters to load multiple tables– Single table adapters to update tables

Use Designer Support– Add a Component to project to hold Adapters– Use designer to create Mappings– Get CommandBuilder Behavior at Compile-

time– Or Map to Stored Procedures (preferred)

Demo

Page 20: ADO.NET Tips and Tricks How to get the most out of your data access…

DataSets & ASP.NETDataSets & ASP.NET

Cache DataSets for non-volatile data– Keep DataSets around for longer than a

Page – Store them in session/global/cache– Expiring Caches are great for DataSets– Growing the Cache as needed with

DataSets

Cache DataSets for non-volatile data– Keep DataSets around for longer than a

Page – Store them in session/global/cache– Expiring Caches are great for DataSets– Growing the Cache as needed with

DataSets

Public Class _default Inherits Page Dim _dataSet As DataSet Const MYDATASET As String = "MYDATASET" Sub OnLoad(...) If Session(MYDATASET) Is Nothing Then _dataSet = New DataSet Else _dataSet = DirectCast(Session(MYDATASET), DataSet) End If '... Session(MYDATASET) = _dataSet

End SubEnd Class

Page 21: ADO.NET Tips and Tricks How to get the most out of your data access…

DataSets and ASP.NET (2)DataSets and ASP.NET (2)

“What caching should I use?”– Session for user specific data

E.g. User preferences, shopping cart, etc.

– Application for app-level data E.g. e-commerce catalog, historical data, etc.

– Cache objects for volatile data E.g. stock prices, weather data, etc.

“What caching should I use?”– Session for user specific data

E.g. User preferences, shopping cart, etc.

– Application for app-level data E.g. e-commerce catalog, historical data, etc.

– Cache objects for volatile data E.g. stock prices, weather data, etc.

Page 22: ADO.NET Tips and Tricks How to get the most out of your data access…

Distributed DataSetsDistributed DataSets

Treat DataSets as messages– Same in Remoting DataSets as Web

Services– Remote the factory that delivers DataSets– When updating, only return changes– Minimize the data across the wire with

Merge

Treat DataSets as messages– Same in Remoting DataSets as Web

Services– Remote the factory that delivers DataSets– When updating, only return changes– Minimize the data across the wire with

MergeClass DataSetFactory 'Remoted Class ' Remotely returns the DataSet Public Function GetData() as DataSet '... End Function

Public Function SaveChanges(ByVal ds as DataSet) as DataSet '... End FunctionEnd Class Dim newDS as DataSet = factory.SaveChanges(ds.GetChanges())

ds.Merge(newDs)

Page 23: ADO.NET Tips and Tricks How to get the most out of your data access…

Distributed DataSets (2)Distributed DataSets (2)

Is fine to use DataSets in Web Services– Default serialization is incorrect

DiffGram Format is Platform Specific

– You must make it an XML Document manually Can use GetXml() or XmlDataDocument

Is fine to use DataSets in Web Services– Default serialization is incorrect

DiffGram Format is Platform Specific

– You must make it an XML Document manually Can use GetXml() or XmlDataDocument

<WebMethod> _Public Function GetData() as XmlDocument Dim ds as new DataSet()

' Fill the DataSet return new XmlDataDocument(ds)End Function

Page 24: ADO.NET Tips and Tricks How to get the most out of your data access…

Distributed DataSets (3)Distributed DataSets (3)

Typed DataSets are great in Web Services– Can refer to .xsd in hand-coded WSDL– While allowed, some toolkits don’t

support it– No way to make ?wsdl do it (at least not

yet)

Typed DataSets are great in Web Services– Can refer to .xsd in hand-coded WSDL– While allowed, some toolkits don’t

support it– No way to make ?wsdl do it (at least not

yet)

<definitions ... xmlns:tds="http://yoursvr/typedds.xsd"> <types> <xsd:complexType ref="tds:yourTypedDataSet" /> </types></definitions>

Page 25: ADO.NET Tips and Tricks How to get the most out of your data access…

Distributed DataSets (4)Distributed DataSets (4)

Remoting may not work as expected– DataSets Remoted by Value

DataSet derive from MarshalByValueComponent

So always remoted by value!

– Remoting DataTables/Rows does not help References to part of DataSet data transmit

entire DataSet

– Remoting DataSets can work Must transmit XML or subset of XML (of the

DataSet)

Remoting may not work as expected– DataSets Remoted by Value

DataSet derive from MarshalByValueComponent

So always remoted by value!

– Remoting DataTables/Rows does not help References to part of DataSet data transmit

entire DataSet

– Remoting DataSets can work Must transmit XML or subset of XML (of the

DataSet)

Page 26: ADO.NET Tips and Tricks How to get the most out of your data access…

TransactionsTransactions

Server Transactions are usually preferable– Generally shorter, therefore better– Server Tx allows results to be returned– Server Tx are not server cursors

Server Transactions are usually preferable– Generally shorter, therefore better– Server Tx allows results to be returned– Server Tx are not server cursors

Page 27: ADO.NET Tips and Tricks How to get the most out of your data access…

Transactions (2)Transactions (2)

Client Transactions Have their Place– Allows a client lock of rows (connected

model)– Connection must be maintained for length

of Tx– Be careful when using them for locking

Scalability and performance will suffer Must have recovery mechanism

Client Transactions Have their Place– Allows a client lock of rows (connected

model)– Connection must be maintained for length

of Tx– Be careful when using them for locking

Scalability and performance will suffer Must have recovery mechanism

Page 28: ADO.NET Tips and Tricks How to get the most out of your data access…

Transactions (3)Transactions (3)Dim tx As SqlTransactionDim cmd As SqlCommandDim rdr As SqlDataReaderTry conn.Open() tx = conn.BeginTransaction()

' Get some Data cmd = conn.CreateCommand() cmd.CommandText = "SELECT * FROM Authors WHERE au_id = '172-32-1176'" cmd.Transaction = tx ' Don’t forget to set the transaction into the cmd rdr = cmd.ExecuteReader()

' Get the zip and close the reader rdr.Read() Dim zip As String = rdr("zip").ToString() rdr.Close()

' Make a change cmd.CommandText = "UPDATE authors SET zip = ‘12345’" cmd.ExecuteNonQuery()

' Commit the Tx tx.Commit()

Catch tx.Rollback()

Finally cmd.Dispose() rdr.Dispose() conn.Close() conn.Dispose()End Try

Page 29: ADO.NET Tips and Tricks How to get the most out of your data access…

Should You Write Vendor Neutral Code?

Should You Write Vendor Neutral Code?

The Promise of Vendor Neutral SQL is wrong– Tuning databases too important– Standard SQL isn’t complete– Only do it if you have a compelling need

You can get half the promise– Write your ADO.NET in a vendor neutral

way– You may need to port the back-end code– Saves you from rewriting it all

The Promise of Vendor Neutral SQL is wrong– Tuning databases too important– Standard SQL isn’t complete– Only do it if you have a compelling need

You can get half the promise– Write your ADO.NET in a vendor neutral

way– You may need to port the back-end code– Saves you from rewriting it all

Page 30: ADO.NET Tips and Tricks How to get the most out of your data access…

Using Vendor Neutral CodeUsing Vendor Neutral Code

Interfaces are key– IDbConnection is a great place to start – Connection Factory to return

IDbConnection– Commands:

IDbConnection.CreateCommand()– Readers: IDbCommand.ExecuteReader()

Interfaces are key– IDbConnection is a great place to start – Connection Factory to return

IDbConnection– Commands:

IDbConnection.CreateCommand()– Readers: IDbCommand.ExecuteReader()

Sub GetReader(SQL as String) as IDataReader ' Connection String Factory Dim conn as IDbConnection = ConnectionStrings.GetConnection()

' Follow the Rest of the model Dim cmd as IDbCommand = conn.CreateCommand() cmd.CommandText = SQL Dim reader as IDataReader = cmd.ExecuteReader() return readerEnd Sub

Page 31: ADO.NET Tips and Tricks How to get the most out of your data access…

Using Vendor Neutral Code (2)Using Vendor Neutral Code (2)

Works with DataAdapters too (mostly)– IDbConnection is a still a great place to

start – No Factory for Data Adapters

unfortunately– Can use the connection to create the

command

Works with DataAdapters too (mostly)– IDbConnection is a still a great place to

start – No Factory for Data Adapters

unfortunately– Can use the connection to create the

command

Sub GetAdapter(SQL as String) as IDbDataAdapter ' Connection String Factory Dim conn as IDbConnection = ConnectionStrings.GetConnection()

' Create the DataAdapter Dim adapter as new SqlDataAdapter()

' Follow the Rest of the model adapter.SelectCommand = conn.CreateCommand() adapter.SelectCommand.CommandText = SQL return adapter}

Page 32: ADO.NET Tips and Tricks How to get the most out of your data access…

Using Vendor Neutral Code (3)Using Vendor Neutral Code (3)

Vendor Specific Features– Can cast to get specific features of an

engine– The more you do, the less vendor neutral

it is– This should be the exception, not the rule

Vendor Specific Features– Can cast to get specific features of an

engine– The more you do, the less vendor neutral

it is– This should be the exception, not the ruleDim cmd as IDbCommand = GetCommand("SELECT * FROM AUTHORS FOR XML;")

If cmd is SqlCommand Then Dim sqlCmd as SqlCommand = DirectCast(cmd, SqlCommand) Dim xmlReader as XmlReader = sqlCmd.ExecuteXmlReader()End If

Page 33: ADO.NET Tips and Tricks How to get the most out of your data access…

ConclusionConclusion

You can write ADO.NET badly Bad ADO.NET not nearly as toxic as

bad ADO DataSets are your friend…really Relationship of DataSets and XSD is

good Distributed DataSets are not that hard CommandBuilders are vaguely evil

You can write ADO.NET badly Bad ADO.NET not nearly as toxic as

bad ADO DataSets are your friend…really Relationship of DataSets and XSD is

good Distributed DataSets are not that hard CommandBuilders are vaguely evil

Page 34: ADO.NET Tips and Tricks How to get the most out of your data access…

Questions?Questions?