ADO .NET
Typing and
Validation
Michael Heron
Introduction
 Our coverage of the ADO .NET framework
in the last lecture focussed on the use of
the data set.
 Disconnected, memory resident
representations of underlying data
structures.
 In this lecture we are going to look at the
data reader.
 Real time query of data.
Data Set Problems
 Data sets are not an ideal system of
accessing underlying databases.
 But then, there’s no such thing as an ideal
solution.
 Data sets are memory hungry.
 The data you have accessed remains resident
in memory as long as your data set is in scope.
 Data sets can be wasteful.
 They execute a full query each time, when you
may not actually need that.
 Data sets are blocking.
 They need to fully complete before you can do
anything else.
Data Readers
 Whereas a dataset is a disconnected
representation, data readers allow for
real-time querying of the underlying
database.
 However, at a cost of flexibility.
 Data readers are forward only, and read
only.
 It accesses data a record at a time.
 Stream-based I/O
Data Readers
 Data readers are platform specific.
 They aren’t generic representations like a data
set.
 As such, they vary depending on whether
there is a managed provider framework.
 As in, is there a supported database engine.
 It cannot access data in more abstract
formats.
 It doesn’t allow for access to XML, or allow you
to build your own representation if you needed
it.
Using A Data Reader
 Data readers can take over the step
where we normally create a data
adapter and data set.
 As usual, it’s created from an SQL
command:
 OleDbCommand
 SqlCommand
 The object you will need is a Data Reader
object:
 OleDbDataReader
 SqlDataReader
Example Code
private void Form1_Load(object sender, EventArgs e)
{
string query;
OleDbConnection oleDbConnection1 =
new OleDbConnection (
"Provider=Microsoft.Jet.OLEDB.4.0;" +
"Data Source=computer_store.mdb;" +
"User Id=admin;Password=;");
OleDbCommand myCommand;
OleDbDataReader myReader;
oleDbConnection1.Open();
query = "select * from Customer";
myCommand = new OleDbCommand(query, oleDbConnection1);
myReader = myCommand.ExecuteReader();
}
Reading From A Reader
 We use the read method of a data
reader to have it read the next record in
the query.
 This returns a true or a false to indicate success.
myReader.read();
 Once we’ve read in a record, we access
it in the reader directly:
txtId.Text =
myReader[“customer id”];
Reading From A Reader
 If we need to read all records, we put it in
a loop:
while(myReader.Read())
{
cmbItems.Items.Add
(myReader["CustomerName"]);
}
Data Reader Mechanics
 Data readers only ever go forward.
 Each invocation of the read method moves
us on one record through the results.
 The data stream persists until the data
reader is disposed of.
 Data readers are useful for quick and
easy access to data.
 And for circumstances where real time
access is important.
Data Sets Revisited
 In our examples to data, we have been
used what are known as untyped
datasets.
 These are data-sets with no compile-time
sanity checks.
 These often cause problems:
 Array out of bounds
 Misspelled fields
Typed Data Sets
 We can actually make .NET handle a lot
of these problems for us at compile time.
 We use Typed Data Sets for this.
 These are a little more complicated to set
up than simple data sets.
 They require us to use the server explorer.
 When we use connection strings, we don’t
need to do that.
Untyped Data Sets
 This is perfectly valid in an untyped Data Set:
myData.Tables[1].Rows[6]
[“Customer Name”]
 What if there’s only one table?
 What if there are only five rows?
 What if there’s no customer name
field?
Typed Data Sets
 Typed data sets put a compile-time check on
element access.
 Plus, you get to use intellisense on them!
 Right click on your project, and choose ‘add new
item’:
Typed Data Sets
 Typed data sets have an extension of xsd.
 Xml Schema Data
 Once you have added that to the
project, you can drag and drop tables
onto the xsd file to create the schema.
 You can also add necessary data relations to
the schema if needed, via the toolbox.
 This creates a class that represents a data
set.
 You use it in place of DataSet.
 This is what happens when you create a data
connection through the wizard.
 Hence why all your data sets end with a number.
Using A Typed Data Set
 Once you’ve gone through the steps of
setting up a type-safe data set, you use it
like a normal data set:
MyDataSet myData;
myAdapter.fill (myData);
 It gets more useful when you actually
come to access the data...
Using a Typed Data Set
 Now, your data set will contain an
enumeration representing the table:
myData.Customer[0];
 And each element of that enumeration
will have a property reflecting a field
name:
myData.Customer[0].CustomerName;
Typed Data Set
 Typed data sets also expose a range of
other useful methods.
 Specifically, each field gets an ‘is null’ method:
for (int i = 0; i < myData.Customer.Count - 1; i++)
{
if (myData.Customer[0].IsCustomerNameNull()
== false)
{
cmbItems.Items.Add
(myData.Customer[i].CustomerName);
}
}
Disadvantages of Strong
Typing
 A typed data set is a layer on top of an
untyped data set.
 As such, it comes with a small performance hit.
 However, this comes with a corresponding
increase in productivity.
 You shouldn’t have to do things that a compiler can
do for you – if a compiler can do them, they are
trivial.
 They are limited by their implementation.
 You don’t want to have to roll one by hand!
 Some people don’t like strong typing.
 These people are wrong.
Advantages of Strong Typing
 Compile-time sanity checking.
 You trade run-time errors for compile-time
errors.
 That’s always a smart trade.
 Intellisense support.
 Seriously, this is major.
 Provision of standard functionality.
 Ease of data access
 Ease of data comparison
Data Readers versus Typed
Datasets
 Which do you want to use?
 Data readers offer performance improvements.
 Data sets are implemented internally with a data
reader.
 This performance gain is usually marginal.
 Data readers require you to build a data
representation yourself.
 When manipulating data, a data set is
more appropriate.
 When doing a quick ‘query then forget’ a
data reader may suffice.
 Your needs will dictate which is best.
Data Validation
 Data validation serves to meet two
requirements.
 Ensure that invalid changes to the
database can be stopped before they are
committed.
 Deal with run-time errors to provide a
seamless user interaction experience.
 Data is valuable.
 Indeed, usually more valuable than any
other element of a system.
Two Mechanics
 Two mechanics exist to allow you to
control data as it enters the system.
 Give the user a chance to validate
information for correctness.
 Validate editing as it is done.
 The data set object gives you a number
of methods for commiting changes.
 RejectChanges
 AcceptChanges
Reject Changes
if (myData.HasChanges()) {
response = MessageBox.Show("Do you want to
“+ “commit these changes?", "Commit
Changes?",
MessageBoxButtons.YesNo);
if (response == DialogResult.No)
{
myData.RejectChanges();
}
else
{
myData.AcceptChanges();
}
}
Change Committal
 Committing changes to the database
is done using update().
 This requires the HasChanges method to
evaluate as true.
 Otherwise, nothing happens.
 We can call it on the data set itself.
 Or on a specific data row if we need finer
grained control.
 Calling it on the data set trickles down
to all internal objects.
 Each DataRow has a RowState
property that holds its internal state.
Accepting Changes
 When you call update, as part of its
standard Operating Procedure, it calls
Accept Changes.
 And this sets the RowState property
appropriately.
 One or the other should be used, rather
than both.
 They don’t Play Nicely together.
Accepting Changes
 Changes must be committed to be
persistent.
 If you have a function that adds a new row
each time you click the button, every time
you click it you’ll lose the last changes.
 This is handled internally by a Data
Versioning system.
 Each row in a table has a property which
handles its current ‘version’.
Data Versioning
 Each row of a table contains multiple
versions of the changed data.
 This is useful for revision control.
 Each row contains the following
properties:
 Current – the current version of the data (after
your changes)
 Original – the data before you started meddlin’
 Proposed – The data that you have modified.
This is only available temporarily
 Default – The default value for the data (usually
the original, or set from the data structure).
Version Control
 This gives you fairly fine-grained control over
committing data to a database.
 You can attach event handlers to various controls
to allow you to control this.
 We can register an event for a changing
row like so:
myTable.RowChanged += new
DataRowChangeEventHandler(Change_
Row);
 Change_Row is the method that will be
executed when this event is triggered.
Event Handlers
 Each event has a special event handler
class to deal with it.
 It serves as a wrapper around a method.
 They stem from either DataRow or
DataColumn
 DataColumnChangeEventHandler
 DataRowChangeEventHandler
 Throw exceptions in these methods if you
want to prevent data being committed.
Event Handler
private static void Change_Row(object sender,
DataRowChangeEventArgs e) {
if ((int)e.Row["CustomerId"] < 0)
{
throw new ArgumentException("Customer ID must “ +
“be positive");
}
}
 Why do this?
 Why not let the database itself handle it?
 This way we can provide meaningful user
feedback.
 And take advantage of strong typing to provide
context and convenience.
Triggering Code
try
{
myTable.Rows.Add(myRow);
}
catch (ArgumentException ex)
{
MessageBox.Show("Customer ID “
“must be positive.");
myTable.RejectChanges();
return;
}
Preventing or Compensating
 Exceptions allow you to catch errors that
you don’t have any explicit control over.
 Users typing into a data grid, for example.
 You also have the chance to validate
within code before you ever trigger an
exception.
 Deal with things in if statements or validation
functions before they get that far.
 There are plusses and minuses to both
approaches.
Conclusion
 ADO .NET gives us the best of both worlds with
database driven applications.
 Strong syntactically correct typing.
 Fine-grained control over updating, removing
and changing data.
 As part of good interface design in any
language, you must commit yourself to
meaningful error messages.
 These are only possible if you have fine-grained
control over validation.

PATTERNS08 - Strong Typing and Data Validation in .NET

  • 1.
  • 2.
    Introduction  Our coverageof the ADO .NET framework in the last lecture focussed on the use of the data set.  Disconnected, memory resident representations of underlying data structures.  In this lecture we are going to look at the data reader.  Real time query of data.
  • 3.
    Data Set Problems Data sets are not an ideal system of accessing underlying databases.  But then, there’s no such thing as an ideal solution.  Data sets are memory hungry.  The data you have accessed remains resident in memory as long as your data set is in scope.  Data sets can be wasteful.  They execute a full query each time, when you may not actually need that.  Data sets are blocking.  They need to fully complete before you can do anything else.
  • 4.
    Data Readers  Whereasa dataset is a disconnected representation, data readers allow for real-time querying of the underlying database.  However, at a cost of flexibility.  Data readers are forward only, and read only.  It accesses data a record at a time.  Stream-based I/O
  • 5.
    Data Readers  Datareaders are platform specific.  They aren’t generic representations like a data set.  As such, they vary depending on whether there is a managed provider framework.  As in, is there a supported database engine.  It cannot access data in more abstract formats.  It doesn’t allow for access to XML, or allow you to build your own representation if you needed it.
  • 6.
    Using A DataReader  Data readers can take over the step where we normally create a data adapter and data set.  As usual, it’s created from an SQL command:  OleDbCommand  SqlCommand  The object you will need is a Data Reader object:  OleDbDataReader  SqlDataReader
  • 7.
    Example Code private voidForm1_Load(object sender, EventArgs e) { string query; OleDbConnection oleDbConnection1 = new OleDbConnection ( "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=computer_store.mdb;" + "User Id=admin;Password=;"); OleDbCommand myCommand; OleDbDataReader myReader; oleDbConnection1.Open(); query = "select * from Customer"; myCommand = new OleDbCommand(query, oleDbConnection1); myReader = myCommand.ExecuteReader(); }
  • 8.
    Reading From AReader  We use the read method of a data reader to have it read the next record in the query.  This returns a true or a false to indicate success. myReader.read();  Once we’ve read in a record, we access it in the reader directly: txtId.Text = myReader[“customer id”];
  • 9.
    Reading From AReader  If we need to read all records, we put it in a loop: while(myReader.Read()) { cmbItems.Items.Add (myReader["CustomerName"]); }
  • 10.
    Data Reader Mechanics Data readers only ever go forward.  Each invocation of the read method moves us on one record through the results.  The data stream persists until the data reader is disposed of.  Data readers are useful for quick and easy access to data.  And for circumstances where real time access is important.
  • 11.
    Data Sets Revisited In our examples to data, we have been used what are known as untyped datasets.  These are data-sets with no compile-time sanity checks.  These often cause problems:  Array out of bounds  Misspelled fields
  • 12.
    Typed Data Sets We can actually make .NET handle a lot of these problems for us at compile time.  We use Typed Data Sets for this.  These are a little more complicated to set up than simple data sets.  They require us to use the server explorer.  When we use connection strings, we don’t need to do that.
  • 13.
    Untyped Data Sets This is perfectly valid in an untyped Data Set: myData.Tables[1].Rows[6] [“Customer Name”]  What if there’s only one table?  What if there are only five rows?  What if there’s no customer name field?
  • 14.
    Typed Data Sets Typed data sets put a compile-time check on element access.  Plus, you get to use intellisense on them!  Right click on your project, and choose ‘add new item’:
  • 15.
    Typed Data Sets Typed data sets have an extension of xsd.  Xml Schema Data  Once you have added that to the project, you can drag and drop tables onto the xsd file to create the schema.  You can also add necessary data relations to the schema if needed, via the toolbox.  This creates a class that represents a data set.  You use it in place of DataSet.  This is what happens when you create a data connection through the wizard.  Hence why all your data sets end with a number.
  • 16.
    Using A TypedData Set  Once you’ve gone through the steps of setting up a type-safe data set, you use it like a normal data set: MyDataSet myData; myAdapter.fill (myData);  It gets more useful when you actually come to access the data...
  • 17.
    Using a TypedData Set  Now, your data set will contain an enumeration representing the table: myData.Customer[0];  And each element of that enumeration will have a property reflecting a field name: myData.Customer[0].CustomerName;
  • 18.
    Typed Data Set Typed data sets also expose a range of other useful methods.  Specifically, each field gets an ‘is null’ method: for (int i = 0; i < myData.Customer.Count - 1; i++) { if (myData.Customer[0].IsCustomerNameNull() == false) { cmbItems.Items.Add (myData.Customer[i].CustomerName); } }
  • 19.
    Disadvantages of Strong Typing A typed data set is a layer on top of an untyped data set.  As such, it comes with a small performance hit.  However, this comes with a corresponding increase in productivity.  You shouldn’t have to do things that a compiler can do for you – if a compiler can do them, they are trivial.  They are limited by their implementation.  You don’t want to have to roll one by hand!  Some people don’t like strong typing.  These people are wrong.
  • 20.
    Advantages of StrongTyping  Compile-time sanity checking.  You trade run-time errors for compile-time errors.  That’s always a smart trade.  Intellisense support.  Seriously, this is major.  Provision of standard functionality.  Ease of data access  Ease of data comparison
  • 21.
    Data Readers versusTyped Datasets  Which do you want to use?  Data readers offer performance improvements.  Data sets are implemented internally with a data reader.  This performance gain is usually marginal.  Data readers require you to build a data representation yourself.  When manipulating data, a data set is more appropriate.  When doing a quick ‘query then forget’ a data reader may suffice.  Your needs will dictate which is best.
  • 22.
    Data Validation  Datavalidation serves to meet two requirements.  Ensure that invalid changes to the database can be stopped before they are committed.  Deal with run-time errors to provide a seamless user interaction experience.  Data is valuable.  Indeed, usually more valuable than any other element of a system.
  • 23.
    Two Mechanics  Twomechanics exist to allow you to control data as it enters the system.  Give the user a chance to validate information for correctness.  Validate editing as it is done.  The data set object gives you a number of methods for commiting changes.  RejectChanges  AcceptChanges
  • 24.
    Reject Changes if (myData.HasChanges()){ response = MessageBox.Show("Do you want to “+ “commit these changes?", "Commit Changes?", MessageBoxButtons.YesNo); if (response == DialogResult.No) { myData.RejectChanges(); } else { myData.AcceptChanges(); } }
  • 25.
    Change Committal  Committingchanges to the database is done using update().  This requires the HasChanges method to evaluate as true.  Otherwise, nothing happens.  We can call it on the data set itself.  Or on a specific data row if we need finer grained control.  Calling it on the data set trickles down to all internal objects.  Each DataRow has a RowState property that holds its internal state.
  • 26.
    Accepting Changes  Whenyou call update, as part of its standard Operating Procedure, it calls Accept Changes.  And this sets the RowState property appropriately.  One or the other should be used, rather than both.  They don’t Play Nicely together.
  • 27.
    Accepting Changes  Changesmust be committed to be persistent.  If you have a function that adds a new row each time you click the button, every time you click it you’ll lose the last changes.  This is handled internally by a Data Versioning system.  Each row in a table has a property which handles its current ‘version’.
  • 28.
    Data Versioning  Eachrow of a table contains multiple versions of the changed data.  This is useful for revision control.  Each row contains the following properties:  Current – the current version of the data (after your changes)  Original – the data before you started meddlin’  Proposed – The data that you have modified. This is only available temporarily  Default – The default value for the data (usually the original, or set from the data structure).
  • 29.
    Version Control  Thisgives you fairly fine-grained control over committing data to a database.  You can attach event handlers to various controls to allow you to control this.  We can register an event for a changing row like so: myTable.RowChanged += new DataRowChangeEventHandler(Change_ Row);  Change_Row is the method that will be executed when this event is triggered.
  • 30.
    Event Handlers  Eachevent has a special event handler class to deal with it.  It serves as a wrapper around a method.  They stem from either DataRow or DataColumn  DataColumnChangeEventHandler  DataRowChangeEventHandler  Throw exceptions in these methods if you want to prevent data being committed.
  • 31.
    Event Handler private staticvoid Change_Row(object sender, DataRowChangeEventArgs e) { if ((int)e.Row["CustomerId"] < 0) { throw new ArgumentException("Customer ID must “ + “be positive"); } }  Why do this?  Why not let the database itself handle it?  This way we can provide meaningful user feedback.  And take advantage of strong typing to provide context and convenience.
  • 32.
    Triggering Code try { myTable.Rows.Add(myRow); } catch (ArgumentExceptionex) { MessageBox.Show("Customer ID “ “must be positive."); myTable.RejectChanges(); return; }
  • 33.
    Preventing or Compensating Exceptions allow you to catch errors that you don’t have any explicit control over.  Users typing into a data grid, for example.  You also have the chance to validate within code before you ever trigger an exception.  Deal with things in if statements or validation functions before they get that far.  There are plusses and minuses to both approaches.
  • 34.
    Conclusion  ADO .NETgives us the best of both worlds with database driven applications.  Strong syntactically correct typing.  Fine-grained control over updating, removing and changing data.  As part of good interface design in any language, you must commit yourself to meaningful error messages.  These are only possible if you have fine-grained control over validation.