December 11, 2008
@ 01:54 AM

NDeavor is a hobby project of mine that I’ve been working on for a couple months now on and off. It’s basically a way for me to explore the things I’m interested in but don’t get to explore in my day job, specifically the latest and greatest from Net like LINQ, extensibility patterns like MEF or System.AddIn, working with database metadata, dependency injection, and code generation. It’s a set of libraries and code that puts a relational database schema in a straight forward object model for the purposes of generating some other artifacts. Boring, I know. Been done before, I know. But never done simply enough (and free enough) to where I could say that my take is unnecessary. Extensibility is currently being provided using the Managed Extensibility Framework (MEF) in which I’ve defined some extremely simple contracts for a provider kind of model for support of all the different database platforms out there. I started with System.AddIn then switched to MEF, but that’s a post in and of it’s own.

Object Model

These are the interfaces, they’re purposely simple, and yet to be finalized:

NDeavor_ObjectModel

The actual contracts aren’t defined on these, as the interfaces are mostly for the purposes of mocking and testing. That, and they are left over from when everything had to be a contract when I was using System.AddIn. Thinking about ditching them in favor of concrete classes.

That’s great, what does it do?

Well on the surface, it’s just a library where any external developer can implement these two interfaces to support a specific database platform. Of course, I’ll be writing providers for the major platforms as that’s the part I”m most interested in.

NDeavor_ProviderInterfaces

Each ISchemaProvider, who’s GetSchema method returns an object that implements IDatabase, can be associated to one IDataTypeProvider, which is used to get more common System.Data.DbType and System.Type mappings from vendor-specific data types. I did say it was simple.

Once you have a provider for NDeavor that supports your database platform of choice, you’ll be able to put it in some directory somewhere and be able to call some code in the NDeavor.Core assembly which will instantiate the plugin, provided you know it’s associated plugin name. After that, you can do anything with the object model that you can do programmatically: use CodeDOM to generate C# or VB code, reverse-engieer SQL scripts, whatever.

The current example of putting NDeavor to work that I’ll be providing is invoking NDeavor via T4 Templates. I wrote a custom T4 Directive Processor which allows you to put a bit of angle bracket text in a T4 template which adds a couple properties to the generated template-class, allowing you to manipulate the model in the ‘code-behind’ of the template itself. For example, this bit of T4 template text:

<#@ template debug="True" language="C#v3.5" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="C:\Program Files\SubSonic\SubSonic 2.1 Final\SubSonic\SubSonic.dll" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="SubSonic" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ include file="TemplateInclude.tt" #>
<#@ DataModel Processor="DataModelDirectiveProcessor" Database="chinook" Name="Chinook" ConnectionStringName="Chinook_MySql" SchemaProvider="NDeavor.SchemaProviders.MySql" #>
<#  IDatabase db = this.ChinookDataModel; 
    IDataTypeProvider prov = this.ChinookDataTypeProvider; #>
using System;
using System.Collections.Generic;

namespace Model
{
<#foreach(ITable table in db.Tables) {#>
    public class <#=GetClassName(table)#>
    {        
<#foreach(IColumn col in table.Columns) {#>
        public <#=prov.GetSystemType(col).FullName#> <#=GetSingularPropertyName(col)#> { get; set; } 
<#}#>
    }
    
<#}#>
}

Generates a bunch of code like this:

    public class Customer
    {        
        public System.Int32 CustomerId { get; set; } 
        public System.String FirstName { get; set; } 
        public System.String LastName { get; set; } 
        public System.String Company { get; set; } 
        public System.String Address { get; set; } 
        public System.String City { get; set; } 
        public System.String State { get; set; } 
        public System.String Country { get; set; } 
        public System.String PostalCode { get; set; } 
        public System.String Phone { get; set; } 
        public System.String Fax { get; set; } 
        public System.String Email { get; set; } 
    }
    
    public class Employee
    {        
        public System.Int32 EmployeeId { get; set; } 
        public System.String LastName { get; set; } 
        public System.String FirstName { get; set; } 
        public System.String Title { get; set; } 
        public System.Int32 ReportsTo { get; set; } 
        public System.DateTime BirthDate { get; set; } 
        public System.DateTime HireDate { get; set; } 
        public System.String Address { get; set; } 
        public System.String City { get; set; } 
        public System.String State { get; set; } 
        public System.String Country { get; set; } 
        public System.String PostalCode { get; set; } 
        public System.String Phone { get; set; } 
        public System.String Fax { get; set; } 
        public System.String Email { get; set; } 
    }

Now that’s what I’m talking about. No querying system tables, no custom vendor-specific api, it’s all done for you in that bit of angle brackets. Booyah. Oh, and I’m using SubSonic solely for the Inflector class to singularlize class names, it’s totally not required for NDeavor. I may provide my own ‘extra’s like that with the T4 functionality, not sure.

Tentative To-Do List

  • Get object model to more polished and developer-friendly state. Includes ditching some of the POCO-ness in favor of read-only collections backed by specific Add/Remove methods, or custom collections all together, I haven’t nailed that down yet.
  • Get 2 providers written. I’m writing the MySql provider in tandem, first as a break from Sql Server which I use daily, and second as a way to get a view of how another platform does things (you flag an integer column to make it unsigned? seriously?), and how those things translate into a generalized framework like this.
  • Figure out configuration of standard plugin directory and how to implement subdirectory watching using MEF.
  • Test. Test. Test.
  • Blog about the ways to access metadata in various database platforms. (MySql 5.x has good, yet incomplete, support of the INFORMATION_SCHEMA ‘standard’, yet I’m using the .GetSchema() methods on the MySqlDataConnection class, via MySql Net Connector 5.2. )
  • Publish to CodePlex when all’s good. (License?)

That’s it for now. More to come!


 
Comments are closed.