dotnetpowered.com - Brian Ritchie's .NET Development Site   [ Home ]

  

Idea: ADO.NET Data Layer
Code Generation for typed connections, commands, data readers, and data sets.

Needs/Requirements

  • True Database Vendor Indepence.  The provider factory capability within Mono gives us the ability to switch providers easily, but it doesn't address the differences in parameter handling etc.
  • Open source code generation framework for typed-datasets.  The typed-dataset generator for .NET is part of the Visual Studio IDE.
  • Fully typed objects for the data layer.  Microsoft's typed-datasets only handle the data in memory.  They don't address executing SQL or calling stored procedures. Also support other "types" of columns such as enumerated and XML. 
  • Better Integration with Mono Provider Factory.  The provider factory creation methods are integrated directly into the framework. 
  • Code generation from XML.   An XML file will define the connection, commands, and results and will be used to generate the code.  Later, a GUI can be built to generate this file.

Overview

  • I've created base classes that all generated classes sub-class from.  The base classes are light-weight wrappers for the standard provider classes: Connection, Command, DataReader, and DataSet.  They implement all of the necessary methods and properties to support the relavent interfaces.
  • The base classes allow for easy extension of the framework outside of the generated code.  Instead of creating static helper methods, the base classes can implement the methods directly.
  • Used XSLT to perform my code generation for a couple of reasons:
    • Cross-platform libraries that don't depend on classes that might not be ready on Linux.
    • Fast, reliable mapping of XML to text output

  

Sample XML File

  • Input file into the code generator
  • Defines connections, commands, enumerated types, and results

<data connection="NorthwindDb" namespace="MyDataLayer">
   <command type="storedproc" name="CustOrderHist" text="CustOrderHist" result="OrderHistory">
      
<param name="CustomerID" type="String" length="5" direction="input"/>
   </command>
  
<result name="OrderHistory" kind="DataSet">
      
<column name="ProductName" type="String" length="5"/>
       
<column name="Total" type="Int32"/>
   </result>

   <command type="text" name="AllEmployees" text="select * from employees" result="EmployeeList"/>
   <result name="EmployeeList">
       <column name="LastName" type="String"/>
       <column name="TitleOfCourtesy" type="TitleOfCourtesy"/>
       <column name="XmlData" type="XmlDocument"/>
   </result>

   <type name="TitleOfCourtesy" kind="enum">
       <value name="Mr" value="Mr."/>
       <value name="Ms" value="Ms."/>
       <value name="Mrs" value="Mrs."/>
       <value name="Dr" value="Dr."/>
   </type>

</data>

 

  

Sample Code to exercise generated classes

  • Opens Typed-Connection
  • Performs a query using a Typed-DataSet
  • Performs a query using a Typed-DataReader

static void Main(string[] args)
{
      ConnectionNorthwindDb conn=new ConnectionNorthwindDb();
      Console.WriteLine("Provider: "+conn.Provider.Description);
      DataSetOrderHistory ds=conn.Command.ExecuteCustOrderHist("ANATR");

      foreach (DataSetOrderHistory.OrderHistoryRow row in ds.OrderHistory.Rows)
      {
            Console.WriteLine("Product {0}: {1}",row.ProductName,row.Total);
      }

      ReaderEmployeeList EmpReader=conn.Command.ExecuteAllEmployees();
      while (EmpReader.Read())
      {
            Console.WriteLine("{0}: {1}",EmpReader.Fields.TitleOfCourtesyToString(),EmpReader.Fields.LastName);
            if (EmpReader.Fields.XmlData!=null)
                  Console.WriteLine(">> Xml: "+EmpReader.Fields.XmlData.OuterXml);
      }
      EmpReader.Close();

      Console.ReadLine();
}