Getting started with MyGeneration, a primer and tutorial
12/03/2009
Phil Gilmore
About MyGeneration
MyGeneration is a developer's code-generation tool that gives you access to your database’s schema. It is useful for building O/RM entity classes based on your database schema.
MyGeneration is mature, free and open-source. It supports many databases. It supports scripting in many languages (C# script VB.NET script, jscript, vbscript, etc.). It supports output into any language. You can write your own templates or use someone else’s.
There are many templates available. Some come with the installer, but many more can be browsed and downloaded from the internet right in the MyGeneration main window.
I found it easy enough to get started using someone else's downloadable templates, so I will only take a brief moment to describe that. I will then spend more time explaining how to write your own templates.
The MyGeneration main page is here and has the latest download link to download.com.
http://www.mygenerationsoftware.com/portal/default.aspx
As the this writing, the download link points here. This download is an NSIS installer package. I recommend installing all components.
When you first run MyGeneration, you are presented with the Default Settings screen. The purpose of this window is to give you a place to define your database connection.
Using the OLEDB configuration wizard (1, see image below), you can easily produce a working connection string. You can also get a generic one here to get your started if you have trouble. Or, enter the string manually if you have the aptitude for it. However you do it, the point is to get the connection string into the connection string field (2).
Once you test your connection string (3) and can connect, type a name for your connection in the box above (4) and save it (5). Now you can always refer to this connection string by selecting it in the dropdown list (4) and clicking the load button. Set the language mapping to your preference (i.e. C#). The other settings are usually left as default. Click the Save button at the top of the page and you may continue.
MyMeta
The MyMeta library is the body that fetches metadata from your database engine. It’s the part that must be aware of your particular database engine in order to support it. You can see what MyMeta can see by opening the MyMeta browser window and viewing its tree contents. If you don’t see your database schema in the MyMeta browser, your connection isn’t set up correctly or you don’t have permissions to see the things you’re looking for. In any case, the data won’t be accessible for code generation unless it is visible in the MyMeta browser.
Templates
There are two ways to do code generation in MyGeneration. The first is to use someone else’s template. The second is to write your own. Most folks who are using a mainstream O/RM such as NHibernate and a mainstream language such as C# can pick a template that works with both and be done in a few minutes. Others can write some templates in a few hours for any systems they want to use, as long as MyMeta supports their database engine.
Using someone else’s templates
In case you want to just get some code generation done and be done with this, I’ve put this here so you don’t have to wade through the rest of this document to get your work done.
MyGeneration’s installer will give you a handful of templates that you can use. These are limited and probably don’t have what you want.
One of the nice things about MyGeneration is that you can browser for more templates online without leaving the application. Click the globe icon in the template browser to find more templates online…
Now we’re talking! You now have the latest templates. It isn’t always easy to find what you’re looking for, but usually they’re categorized pretty well. You may look for a template by the language you want to generate, or you may search by the database engine or data access framework you’re using, such as NHibernate.
Double-click a template to see some pretty good detail about it if you need to know more about it. When you’ve found a template you like, click the save button and continue for more templates or close the Online Template Library window when you’re done.
The templates you saved will now appear in your regular Template Browser. You can right-click on the desired template in the Template Browser, click EXECUTE from the popup menu, and code generation will begin.
What happens at that point depends on the template. Many templates will need you to give them some input parameters to do their job. When this is the case, they will prompt you for parameters with their own popup form. When it is complete, you will have output in the output window, or the output will be saved to a set of files. This is also specific to the template. Most templates that output to files will prompt you for an output location.
Modifying someone else’s template
If you followed the steps above in “Using someone else’s templates”, you know how to get your hands on a template that you can modify. Take any template in your template browser and double-click it. The template can be modified right inside the MyGeneration application. If you’re modifying someone else’s template, I cannot give you very specific direction. From here on, I will only give direction about how to create a new template.
Creating your own template
Relax. This really is easy. You can pick from several languages and probably won’t have to learn a new language to do this. There is code insight, useful error messages and bugs are easy to fix.
Here some more information about MyGeneration that you will need to know. First, MyGeneration has several systems. We’ve already covered MyMeta, MyMeta Browser, Template Browser, and Online Template Browser. There are separate help files for different systems too. This is useful to know because you will be using these help files from time to time. The help files really have good information, but it can be hard to find what you’re looking for, especially if you don’t know which help file to look in.
The systems you’re primarily concerned with are Zeus, and MyMeta. Unless you are using any of the other systems listed under Help.
Zeus is the scripting engine that MyGeneration uses. It is the engine that takes your templates, executes them to create a program for code generation and then executes that code generation program and either passes the output back to MyGeneration or outputs it to a file (if the script tells it to). Zeus can use several languages. It supports C# Script, VB.Net Script, VBScript and JScript. I will proceed under the assumption that you are using C# Script.
MyMeta, as we have discussed, is a set of objects that your Zeus script can ask for when it needs information about your database schema. There are many object types in this library and you will probably want to just browse over the help file for it to familiarize yourself with some of the interfaces that you will be using. They are mostly self-evident.
The Zeus engine has an output object that to which the generated code will be written. This output would be sent to the Output window by default. However, many templates will redirect this output to a file. If that happens, the output may not appear in the output window.
The Zeus engine has an input object that can be queried for parameters that the user enters are runtime. Runtime is the time at which the Zeus script is executed. Using the input object is optional.
To create a new template, click File, then New –> C# Zeus Template. The template will appear in a new tab on the main MyGeneration panel. Inside that “Untitled” panel are 4 more tabs.
Tab | Description |
Template Code | You write this script. This script builds the Template Source script. |
Interface Code | You write this script. It builds a UI for gathering parameters from the user. |
Template Source | This script is generated and you do not touch it. This is the code generation program. This is the output of the Template Code script. Look here if you have errors if your Template Code script. |
Interface Source | This script is generated and you do not touch it. As the TemplateCode generates the Template Source, so too does the Interface Code generate the Interface Source. Use this to debug your Interface Code. |
Output | If the output from the Template Source is not redirected to a file, you will see it here when you execute the template. |
As you can see, you will do your editing primarily in the template code tab. If you build a UI, you will also use the interface code tab.
In its simplest form, a template can be comprised simply of template code which accesses the MyMeta object as input and the output object as output. Next, you will want to redirect output to files, then finally build a UI. The data gathered in the UI is made available to the template code via the input object.
Let’s start with something simple. In the Template Code and Interface Code tabs, you will see some default code. Keep it. You will need it. Run the template (F5). You see the output in the output tab. Great! Back at the Template Code window, find the Render() method. This is where you will write your code. It is your entry point. You can write your own methods outside of it and call them from the Render method.
Server tags
<% and %> tags define blocks of code within the Template Code. As in ASP or JSP, this is technically a code file, but anything outside the server tags is output as a literal. Note that in the Template Code window, there is literal region that looks like this:
%>
You can toggle in and out of script like this
<%
Take a look at the Template Source tab. You’ll see that this literal has been converted to this:
output.writeln("You can toggle in out of script like this");
To keep things simple for now, I removed indentation of this literal in the template code window. You will find that line breaks and tabs will be adhered to in these literal regions. For finer control, you can use direct output in the Template Code like this:
output.writeln("You can toggle in out of script like this");
%>
You can toggle in out of script like this
<%
The output becomes:
output.writeln("You can toggle in out of script like this");
output.writeln("");
output.writeln("You can toggle in out of script like this");
Of course, if you only did your literal output in this way, your template code would be identical to your template source. But until you get used to it, you can do this to fine tune your whitespace.
You can also dereference a single symbol from your code without using a call to output.writeln() using the <%= %> tags. For example:
int keyCount = input.Keys.Count;
int j = 0;
foreach (string inputKey in input.Keys)
{
j++;
string inputValue = input[inputKey].ToString();
%>Input key <%=j.ToString()%><%
%> of <%=keyCount.ToString()%><%
%>: <%=inputKey%><%
%> = <%=inputValue%><%="\r\n"%><%
}
The Dereference Symbol tag pair is equivalent to the output.writeln. Notice that you never put a semicolon between these tags. To do so would generate something like this in the Template Source. So this tag:
<%=j.ToString();%>
Would generate this:
output.write(j.ToString(););
While it’s obvious to see how the premature semicolon breaks the generated code, it is still sometimes hard to remember to omit your trailing semicolons when using the Dererence Symbol tags. Understanding that the text is output verbatim to an output.writeln() call makes it easier to keep things straight.
Using MyMeta
Okay, so we could output anything we want now, but we don’t have much to do without some input. The input is going to start from our database schema. This comes from the MyMeta object. MyMeta is a global object instance of the MyMeta library and is available throughout your script. Have a look at what it can do…
The databases property will give you a collection of databases. From there, you can drill down into tables, columns, keys, stored procedures, etc. To get a single database, first you have to access it by index from the MyMeta.Databases collection.
MyMeta.Databases["Northwind"]
Type the line above in the Render method. This references the Northwind database. For RDBMS servers like Oracle, there may be one or many databases. For local databases, this may not apply.
Now that we are referencing a database, you should be able to reference schema information for the tables on that database. You can drill down from the database reference to its tables collection. But lo, press the period key at the end of that line, and no code insight comes up. The code insight is useful but limited. It does not show you types, and sometimes it doesn’t know the type of the subject and can’t show its members.
Workaround – If you assign that reference to a strongly typed variable, the variable gives the code insight enough information to show you its members. There’s just one problem. You don’t know the type either. You’ll have to guess. This sounds terrible, but it’s not that bad. Remember I said you need to be familiar with the help files? Open the MyMeta help file from the MyGeneration help menu.
Right there in the root you can see a list of interfaces provided by MyMeta. You can easily guess which type our reference is. It is an IDatabase.
Now we have the next level of code insight. This is only required for code insight. Instead of this (which provides code insight):
IDatabase db = MyMeta.Databases["Northwind"];
ITable tbl = db.Tables["table1"];
IColumn col = tbl.Columns["id"];
output.writeln(col.Name);
This would work too. But you have to be more familiar with MyMeta object hierarchies.
output.writeln(
MyMeta
.Databases["Northwind"]
.Tables["table1"]
.Columns["id"].Name
);
Most collections in MyMeta have named members and can be accessed by ordinal index or by named index:
MyMeta.Databases[0];
MyMeta.Databases[“Northwind”];
You now have all the tools you need to build and run a useful code generation template using MyGeneration.
Redirecting output
Expect to be executing your templates repeatedly during development of the template itself and periodically during development of whatever project consumes your generated code. It becomes cumbersome to copy the contents of the output window from the clipboard (MyGeneration automatically copies it to the clipboard when it writes to the output window) over to an editor to overwrite previous output. It would be convenient to write output directly to a file on disk. The output object can do this. It can also write to multiple files.
For more detail on the output object, see the Zeus help files under IZeusOutput and ZeusOutput class. But here is a basic use of output to files.
// At first, our output buffer is empty. Add something to it.
output.writeln(“Something”);
// Save the current buffer to 2 different files.
output.save(path1, “o”);
output.save(path2, “o”);
// Start a new buffer and save it to a third file.
output.clear();
%>Something else<%
output.save(path3, “o”);
// Start a new buffer and since we don’t clear it, it shows up in the output window.
output.clear();
%>Wrote to: <%=path1%><%
%>Wrote to: <%=path2%><%
%>Wrote to: <%=path3%><%
You may have noticed that I’m mixing %>literals<% (outside the server code) with explicit writeln.output() calls. Remember that these are interchangable and you’ll want to shoot for consistency most of the time. I just mixed them up to show some variety in my examples.
The second parameter of the save() method (“o”) is not documented under IZeusOutput very well. They are found under the ZeusOutput class, but poorly. I’ll document them here to save you time.
Value | Description |
“o” | Overwrites an existing file. |
“b” | Overwrites existing file but backs it up first. |
“a” | Appends an existing file. |
“d” | Do not overwrite (silent failure). |
Building a UI for your template.
If you need parameters for your template execution they can be gathered at runtime by a UI which you build into your template. The UI code is written in the editor under the Interface Code tab. Again, the default sample code should be kept to get you started. The Setup() method is your entry point. In here, you have a global object named ui. It is of type Zeus.UserInterface.GuiController. You will find help for it in Zeus help under Zeus.UserInterface.
In a nutshell, you have some Addxxxx methods to create UI elements such as buttons and comboboxes. These methods return objects of the corresponding type (IGuidButton, etc.). Those objects then have layout properties such as top, bottom, width, etc. To keep things simple, the IGuiController ui is very good as choosing default layout parameters for you. if you add an element to the UI, it will appear in vertical order relative to other elements you have added. It will fill the form horizontally. While this isn’t always pretty, it makes it easy to get started.
First thing’s first. The interface is disabled for now. Uncomment the contents of the Setup() method to see what happens.
public override void Setup()
{
ui.Width = 100;
ui.Height = 100;
GuiLabel lblDemo = ui.AddLabel("lblDemo", "Demo", "Demo Tooltip");
ui.ShowGui = true;
}
That wasn’t hard. The code sizes the form (lines 1 and 2), adds a label (line 3) and displays the form (line4). To expound on this, we’ll keep lines 1, 2, and 4. We’ll increase the size of the form and replace line 3 with our customized form code.
public override void Setup()
{
ui.Width = 500;
ui.Height = 300;
// Customize things here...
ui.ShowGui = true;
}
Next we’ll add some controls that are actually going to be useful in this sort of project. We’ll add a database selection combo box and an output file selector (with labels). I’ll add them to their own method to keep the Setup() method cleaner. Remember that the ui object creates the controls for us. This is a bit easier than MyMeta. These functions have code insight that tells us the return type.
Easy enough? This won’t take long.
public void CreateControls()
{
// Database selection drop-down.
GuiLabel lblSelectDatabase =
ui.AddLabel("lblSelectDatabase", "Select a database:", "");
GuiComboBox cboSelectDatabase =
ui.AddComboBox("cboSelectDatabase", "Select a database");
// Output file picker
GuiLabel lblOutputFile =
ui.AddLabel("lblOutputFile", "Output filename: ", "");
GuiTextBox txtOutputFile =
ui.AddTextBox("txtOutputFile", "", "Output filename");
GuiFilePicker fpOutputFile =
ui.AddFilePicker("fpOutputFile", "...", "", "txtOutputFile", false);
}
Notice that when passing a control as a parameter to an Addxxx() method, it expects the control name as a string, not the object reference for that control. To keep things straight, I always make the string ID parameter which I pass to the add method identical to the object reference to which I assign the ui element (lblOutputfile = ui.AddLabel(“lblOutputFile”…) for example).
Well, that creates the form controls, but they have no data behind them. You will frequently need to bind MyMeta elements to your UI elements. We will want to do so with our database selection combo box.
cboSelectDatabase.BindData(MyMeta.Databases);
How easy was that? The layout is still a little ugly, but we now have a working UI. The next step is to use it from the Template Code. We’ll get to that in a moment.
UI Layout
Right now, let’s fix up the layout a bit for sanity’s sake. This is easy too. Just set the width, height, top and left properties of each UI element. You can calculate them in conjunction with the width and height properties of the ui object.
Here is the finished UI. As simple as it is, it may be all you need for some projects.
public class GeneratedGui : DotNetScriptGui
{
// Declare UI elements in class scope.
GuiLabel lblSelectDatabase;
GuiComboBox cboSelectDatabase;
GuiLabel lblOutputFile;
GuiTextBox txtOutputFile;
GuiFilePicker fpOutputFile;
public GeneratedGui(ZeusContext context) : base(context) {}
//-----------------------------------------
// The User Interface Entry Point
//-----------------------------------------
public override void Setup()
{
ui.Width = 500;
ui.Height = 300;
ui.Title = "Building our first UI!";
// Customize things here...
CreateControls();
LayoutControls();
DataBindControls();
ui.ShowGui = true;
}
public void CreateControls()
{
// Database selection drop-down.
lblSelectDatabase =
ui.AddLabel("lblSelectDatabase", "Select a database:", "");
cboSelectDatabase =
ui.AddComboBox("cboSelectDatabase", "Select a database");
// Output file picker
lblOutputFile =
ui.AddLabel("lblOutputFile", "Output filename: ", "");
txtOutputFile =
ui.AddTextBox("txtOutputFile", "", "Output filename");
fpOutputFile =
ui.AddFilePicker("fpOutputFile", "...", "", "txtOutputFile", false);
}
public void LayoutControls()
{
// Form
ui.Width = 500;
ui.Height = 150;
ui.Title = "Building our first UI!";
int margin = 12;
// Position dropdown and label.
lblSelectDatabase.Width = 100;
lblSelectDatabase.Left = margin;
lblSelectDatabase.Top = margin;
cboSelectDatabase.Width = ui.Width
- lblSelectDatabase.Width - margin * 3;
cboSelectDatabase.Top = lblSelectDatabase.Top;
cboSelectDatabase.Left = lblSelectDatabase.Left
+ lblSelectDatabase.Width + margin ;
// Position file picker
lblOutputFile.Width = lblSelectDatabase.Width;
lblOutputFile.Left = lblSelectDatabase.Left;
lblOutputFile.Top = lblSelectDatabase.Top
+ lblSelectDatabase.Height + margin;
fpOutputFile.Width = 25;
fpOutputFile.Top = lblOutputFile.Top;
fpOutputFile.Left = ui.Width - fpOutputFile.Width - margin;
txtOutputFile.Width = ui.Width
- fpOutputFile.Width - lblOutputFile.Width - margin * 4;
txtOutputFile.Top = lblOutputFile.Top;
txtOutputFile.Left = cboSelectDatabase.Left;
}
public void DataBindControls()
{
cboSelectDatabase.BindData(MyMeta.Databases);
}
}
Consuming the UI, Method 1: Input
You have a working UI now but what good is it? What does it actually do? By itself, it lets you specify parameters but those parameters have to be consume by the Template Code script. It does so in two ways. I’ve mentioned before that the first way is through the input object. The input object is also a global instance. Once again, see the Zeus help file under IZeusInput and ZeusInput. The input
object provides the values for the UI elements as selected by the user at runtime. It is more convenient than the second method, which is to access the elements themselves. It is more convenient because every element has a single scalar value. For example, a dropdown list has a list of elements, a selected value, a selected index, etc. The input object contains a single string for that control, the selected value.
The input object is a collection of values, each with a name corresponding to a control on the UI form (more specifically the string ID property used in the construction of said control) and a value as entered or selected in that control at runtime. Objects retrieved from this collection must be cast to specific types, usually string.
Here’s an interesting exercise. Put this code in your Template Code’s Render method. It will output all the items in the input collection. Now you’ll know exactly what’s available to you. You’ll see the elements from your UI as well as several fields defined by MyGeneration. MyGeneration input key names begin with double underscores.
output.writeln("Input fields: ");
foreach(string s in input.Keys)
{
output.write(s);
try
{
output.write("=");
output.write((string)input);
}
catch { }
output.writeln("");
}
You’ll also see an entry for cboSelectDatabase. It is as you selected in the UI. You can now use this in your template code like this:
IDatabase selectedDB =
MyMeta.Databases[(string)input["cboSelectDatabase"]];
output.writeln("Selected database: " + selectedDB.Name);
output.writeln("");
foreach (ITable t in selectedDB.Tables)
{
output.writeln("\tTable: " + t.Name);
foreach(IColumn c in t.Columns)
{
output.write("\t\tColumn: name=" + c.Name);
output.write(", dbTargetType=" + c.DbTargetType);
output.write(", DataType=" + c.DataType);
output.write(", DataTypeName=" + c.DataTypeName);
output.writeln("");
}
output.writeln("");
}
That script will output basic schema for all tables in your database.
Notice that when retrieving the value of the cboSelectDatabase combo box, we did not have to drill into a SelectedValue property to get the selected value. The input object gives us a single string for cboSelectDatabase when we pass its name as a key. The string is its selected value.
Consuming the UI, Method 2: UI
The second method is discouraged. You can use the ui object to retrieve the UI elements directly and query their properties for the values you want.
In the previous example, we might have changed this line:
IDatabase selectedDB =
MyMeta.Databases[(string)input["cboSelectDatabase"]];
and made it work like this:
IDatabase selectedDB =
MyMeta.Databases[((GuiComboBox)ui["cboSelectDatabase"]).SelectedValue];
When I change that line, the rest of the template code still works.
I hope this has given you everything you need to get started. It certainly won’t cover everything you need to learn to achieve your particular task, but my goal was to get you started so that you would know where to look and have the tools to troubleshoot your work. If I’ve left out something fundamental, please let me know.
Phil Gilmore (www.interactiveasp.net)