Home   eXtremeArchitecture   Spice   ip-poster   gWizard   Connacht   Contact  


1. gWizard Architecture

There are a number of approaches to object-relational mappings, the essential differences centring on which end of the mapping "drives" the process, and the end product (code generation versus dynamic configuration).

Each approach has it's drawbacks. If the database tables are used to drive the code generation process, additional information must typically be supplied to identify such things as derived class and lookup fields. Also many object purist would argue that design should proceed from the object model to the database implementation, not the other way around. However driving code generation from the object end is also problematic as it requires an object description such as UML XMI, Java bytecode or Microsoft MSIL. Java bytecode and MSIL tie the code generator to a particular technology. UML XMI is complex and expensive to implement, and it does not naturally describe some constructs such as lookup fields well.

Ideally an object-relational mapper should be able to drive the process from either end, importing both relational and object meta-data in various formats, resolving any conflicts and storing the combined data in a repository. (See below) Ideally it should also be free and open source.

Connacht implements table driven code generation for pragmatic reasons: (1) the author and many others have easy access to database design tools, or have existing databases that could benefit from automatically generated business objects, and (2) the authors resources are limited and he feels that it is too easy to get bogged down with many of the technicalities of the other approaches (e.g. UML XMI).

Version 0.9b: gWzard does not allow users to create the own object definitions. All object definition must be imported from (synchronised with) a database. The aspects of the object description that can be edited are discussed below.

Version 0.9b: gWizard does not attempt to model inheritance hierarchies in any way.

2. Usage

2.1 Operations Supported at the Command Line - How To

Invoke the GUI

Type the following at the command line :     gWizard /P

Generate Code

To generate code for a specific object, type the following at the command line :     gWizard /O object_name

2.2 Operations Supported by the GUI - How To

Synchronise with Database
Generate Code

3. Internal Data Structures

The diagram below shows the data structures used to describe business objects and code generators. 

3.1 Persistence

The structures above are serialised to XML by the Project.Save() operation and recreated by Project.Load(), with the exception of the CodeGenerators which are disconnected from the project before serialisation and reconnected afterwards. Serialisation is performed using .NET XML serialization services on the project object.

3.2 Database Synchronisation

Object descriptions can be synchronised with (imported from) the database at the project and object level using the SynchroniseWithDB() methods.

3.3 Editing Object Descriptions

3.3.1 Attributes

Version 0.9b: The object attributes cannot be edited and must be generated by synchronising the object with the database.

3.3.2 Primary Key

Version 0.9b: The primary key associated with an object cannot be edited and must be generated by synchronising the object with the database.

3.3.3 Foreign Keys

Version 0.9b: Foreign keys associated with an object cannot be edited and must be generated by Synchronising the object with the database.

3.3.4 Excludes

It is sometimes necessary to exclude special fields from automatically generated code. Examples include user passwords and timestamps that record specific operations. An attributes is excluded from code generation on an object or per generator basis by dragging the attribute and dropping it on the appropiate Exclude node.

3.3.5 Lookups

Business objects are often not perfect reflections of their database images. For example, a Contract business object may contain both the unique ID (foreign key) of a Company and the Company name (text).  The Company name would not appear in the Contracts table in the database (it would be moved to the Companies table as part of the usual normalisation process).

Fields such as Company name are referred to in GWizard as lookup fields. Lookup fields can be implemented at the business object or database level. The object description used by GWizard is capable of supporting both. 

Version 0.9b: The GWizard code generators assume that lookup fields are implemented in the database (stored procedures). There are some potential scalability problems with this approach but it is unlikely that the majority of users (the author :-)) will ever encounter them.

3.4 Editing Other Information

3.4.1 Context Fields

These are fields that may be required to identify whether or not operation should go ahead, and/or to determine which database records are visible. For example, an accounting application may filter on CompanyID. Accounting records that are not 

Version 0.9b: The built-in code generators pass the context fields to all object methods and stored procedures.

3.5 Editing Summary

The following table may be of assistance if gWizard does not behave as you expect.

3.6 Versioning

The versioning strategy is delegated completely to the code generators. A code generator may or may not require specific fields to be identified in which case it should create appropriate entries in the RHS list view, or assume an existing database naming convention. 

3.7 Code Generation

3.7.1 Code Generator Interfaces

Code Generators are specificed using the data structure below. The Name and Module fields correspond to the .NET class and assebly names. The CodeGeneratorRef.Load() method loads the specified code generator and points the CodeGenerator field to it. The CodeGeneratorRef.Unload() free releases all reference to the code generator and nulls the CodeGenerator field. The CodeGeneratorRef.GetDescription() method queries the CodeGenerator for a description if loaded, otherwise it generates a composite name from the Name and Module fields.

	public class CodeGeneratorRef
	{
		public string Name;
		public string Module = "GWizard";
		public string GetDescription() {...}
		public void Load() {...}
		void Unload() {...}
		public CodeGenerator CodeGenerator = null;
	}

Code Generators must derive from the abstract CodeGenerator class shown below. An abstract class is used rather than an interface as .NET serilisation services throw exceptions if a pointer to an interface is encountered.

	public abstract class CodeGenerator
	{
		public abstract CodeGenerationInfo GetSettingsStruct(CodeGeneratorRef p, ObjectInfo objectInfo);
		public abstract void GenerateCode(ObjectInfo objectInfo, CodeGenerationInfo settings, ArrayList context, Logger logger);
		public abstract string Description {get;}
	}

Code Generator configuration information must be derived from the CodeGenerationInfo class shown below. 

	public class Property
	{
		public enum PropertyType {eString, eInteger, eBoolean, eDateTime, eSqlDBType}
		public Property() {}
		public Property(string name, PropertyType type, string szvalue) {...}
		public string name;
		public PropertyType type;
		public string szvalue;
		public bool readOnly = false;
	}

      	public abstract class CodeGenerationInfo
	{
		public CodeGenerationInfo() {}
		public CodeGenerationInfo(CodeGeneratorRef codeGenerator, ObjectInfo objectInfo) 
		{
			this.p = codeGenerator; 
			this.ObjectInfo = objectInfo;
		}
		public CodeGeneratorRef p = null;
		public ObjectInfo ObjectInfo = null;
		public ArrayList Exclude = new ArrayList();

		public abstract void CreateListView(ProjectView projectView);
		public abstract void SetValue(Property property);
		public Property GetProperty(string propertyName) 
		{
			if(properties == null) 
				return null;
			for(int i=0; i<properties.Length; i++)
			{
				if(propertyName.Equals(properties[i].name))
					return properties[i];
			}
			Debug.Assert(false); // Not found.
			return null; 
		}
		public Property[] properties = null;
	}

3.7.2 Database Interface

Database Adapters must implement the GWizard.IMeta interface shown below.

	interface IMeta
	{
		void GetTableMetaData(string connectionString, ObjectInfo objectInfo);
		ArrayList GetTables(string connectionString);
	}

IMeta.GetTables 

Synopsis: Must return a list of all user table names.

Parameters:
connectionString - should contain enough information for the adapter to connect to the database and query for users table names.

IMeta.GetTableMetaData
 

Synopsis: Queries the database for meta information such as column descriptions, primary keys, foreign keys and identity columns, and returns the information as a ObjectInfo structure.

Parameters:
connectionString - should contain enough information for the adapter to connect to the database and query for meta-data.
objectInfo - an existing ObjectInfo object. The objectInfo.table data member specifies the database table for which meta-information is required.

 

SourceForge Logo