Frans Bouma said:
This is truly not unique. My predicate objects are checked at
compile
Ill post line by line - but this is what I was working on now:
ADO.NET is a good library for connecting to databases. But using ADO.NET still uses the standard
methodology that data connectivity is not type safe, and that the binding to the database is loose.
Fields are bound using string literals, or numeric indexes. All types are typecast to the desired
types. Changes to the database will introduce bugs into the application. These bugs however will
not be found until run time because of the loose binding. Unless every execution point and logic
combination can be executed in a test, often bugs will not appear until a customer finds them.
Because of this, as developers we have been conditioned to never ever change a database. This
causes databases to be inefficient, contain old data, contain duplicate data, and contain many
hacks to add new functionality. In fact this is an incorrect approach, but we've all grown
accustomed to accepting this as a fact of development.
However if we use a tight bound approach as we do with our other code, we can upgrade and update
our database to grow with our system. Simply change the database, and recompile. Your system
will find all newly created conflicts at compile time and the functionality can then be easily
altered to meet the new demand. I call this an "Extreme Database" or XDB, inline with Extreme
Programming or XP.
Using the built in ADO.NET commands reading from a query is as follows:
IDbCommand xCmd = _DB.DbConnection.CreateCommand();
xCmd.CommandText = "select \"CustomerID\", \"NameLast\", \"CountryName\""
+ " from \"Customer\" C"
+ " join \"Country\" Y on Y.\"CountryID\" = C.\"CountryID\""
+ " where \"NameLast\" = @PNameLast1 or \"NameLast\" = @PNameLast2";
xCmd.Connection = _DB.DbConnection;
xCmd.Transaction = xTx.DbTransaction;
IDbDataParameter xParam1 = xCmd.CreateParameter();
xParam1.ParameterName = "@NameLast1";
xParam1.Value = "Hower";
xCmd.Parameters.Add(xParam1);
IDbDataParameter xParam2 = xCmd.CreateParameter();
xParam2.ParameterName = "@PNameLast2";
xParam2.Value = "Hauer";
xCmd.Parameters.Add(xParam2);
using (IDataReader xReader = xCmd.ExecuteReader()) {
while (xReader.Read()) {
Console.WriteLine(xReader["CustomerID"] + ": " + xReader["CountryName"]);
}
}
The same code when written using Indy.Data is as follows:
using (CustomerQry xCustomers = new CustomerQry(_DB)) {
xCustomers.Where = CustomerQry.Col.NameLast == "Hower"
| CustomerQry.Col.NameLast == "Hauer";
foreach (CustomerQryRow xCustomer in xCustomers) {
Console.WriteLine(xCustomer.CustomerID + ": " + xCustomer.CountryName);
}
}
First lets ignore the fact that it is shorter. The standard ADO.NET could be wrapped in an objects
or method as well. What I want to point out is the fact that the standard ADO.NET requires the use
of text strings. In fact, the code listed above has a mistake in it. "@NameLast1" should be
"@PNameLast1". The error in the standard ADO.NET code will not be detected until the code is
executed, and tracing it down will be more difficult. While any mistakes in the Indy.Data code
will be caught immediately by the compiler, and the exact location pinpointed. This allows for
databases to be easily evolved during development, and greatly reduces bugs as mistakes are
found and pinpointed during compile.
Could this be done with a codegen or O/R mapper? Yes, but generally they offer one of the
following:
1) Method calls that create an awkward syntax not representative of the where clause,
especially when complex ones are encountered.
2) Strings arguments, which leaves us again with a late bound interface causing bugs to only be
found at runtime.
3) Database Parameters - Parameters can give type safety and early binding if interpreted
properly which many tools not only do, but rely on this behaviour. The problem is that parameters
severely limit the flexibility of the where clause and often cause many versions of a single
object to be created with many variations of parameters.
With Indy.Data's approach, full freedom is retained to form the where clause as needed and
still retain all the benefits of type safety, early binding, and clean syntax.