DBLGen uses StringTemplate as the main plugin for exporting code. StringTemplate has a lot of functionality that I won't cover here because there is too much to write about.
Since this is the first version of this documentation, I ask you to also take a look at the sample provided in the binary package, so you can see how to do some things. The current version uses the C# version of StringTemplate 3.0.1, and you can check the full documentation here.
A template file for DBLGen is what StringTemplate calls a "Group File". That is, a file with .stg extension, that defines one or more templates inside. A template is similar to a function that outputs some text, and it receives zero or more parameters as arguments.
Every template needs a name, a formal argument list (even if it is empty) and a definition of the contents. Single-line template contents are defined within double quotes (" ... ") while multi-line contents are defined between << and >>.
The contents of the template can access attributes defined by DBLGen. You can do this using <attributeName> syntax. Some attributes have child attributes, so you can access them using the dot operator, like this: <attribute.otherAttribute.lastAttribute>. Inside the contents, you can use the backslash (\) as escape to print a character that has special meaning to string template, like \\ for \, \< for < or \> for >.
For DBLGen plugin, we need to create a group file with at least 2 defined templates, one for the output filename and another one for the contents.
This is the basic syntax for a DBLGen template file:
group <GROUP NAME> <PREFIX>_FileName(table) ::= "OutputFileName.txt" <PREFIX>_Contents(table) ::= << Contents >>
The group name and prefixes you use doesn't matter to the template output. The group name is needed because of StringTemplate syntax, and the prefixes are used only to match the contents with the filename. You can define multiple templates in the same file by using multiple name/content pairs. Just remember you MUST define both templates ate least once.
This is a simple file that outputs the table name in camel case:
group MySample; Sample_FileName(table) ::= "MySample.txt" Sample_Contents(table) ::= << Hello from <table.name.camel>! >>
Also, you can define a folder structure in the filename, so you can export multiple files to the right directory:
group MultipleFiles; /* Business Object */ BO_FileName(table) ::= "BusinessObjects\\<table.alias>.cs" BO_Contents(table) ::= << public class <table.alias> : MyBaseClass { ... } >> /* Collection */ Collection_FileName(table) ::= "BusinessObjects\\<table.alias>Collection.cs" Collection_Contents(table) ::= << public class <table.alias>Collection : Collection\<<table.alias>\> { ... } >> /* Data Access Layer */ DAL_FileName(table) ::= "DAL\\<table.alias>DAL.cs" DAL_Contents(table) ::= << public class <table.alias>Collection : MyBaseDAL { ... } >>
Some attributes are lists of objects, so you need to iterate through them to get your result. A simple way of achieving this is using this construct:
Sample_Contents(table) ::= << ... <table.columns:mySubTemplate()> ... >> mySubTemplate(column) :: << private <column.clrtype> <column.name>;" >>
This will apply a new template called mySubTemplate for each column in the list.
Subtemplates are ideal for big multiline templates, but single line templates can be defined in a simpler way:
Sample_Contents(table) ::= << ... <table.columns:{ c | private <c.clrtype> <c.alias>; }; separator="\r\n"> ... >>
This solution uses a lambda function to define an anonymous template. We declare a name for the argument ("c"), and then we declare the template directly after the pipe. You can also declare no argument. StringTemplate will use the "it" name to identify it:
Sample_Contents(table) ::= << ... <table.columns:{ private <it.clrtype> <it.alias>; }; separator="\r\n"> ... >>
As you may be wondering, the "separator="\r\n"" in the end is one of the many options you can apply to the loop. For now, I ask you to take a look at the StringTemplate manual for a better reference.
Here is a list of attributes that you can use on template:
attribute | type | description |
---|---|---|
name | string | |
schema | string | |
fullname | string | schema.name |
alias | string | |
comments | string | |
description | string | |
columns | list of Columns | all columns |
pkcolumns | list of Columns | all columns that are primary keys |
fkcolumns | list of Columns | all columns that are foreign keys |
nonpkcolumns | list of Columns | all columns that are not primary keys |
attribute | type | description |
---|---|---|
name | string | |
alias | string | |
comments | string | |
description | string | |
clrtype | string | |
sqltype | string | |
type | string | |
length | number | |
precision | number | |
scale | number | |
isnullable | boolean | true if the column accepts null |
isreadonly | boolean | true if the column is read only |
ispk | boolean | true if the column is a primary key |
isfk | boolean | true if the column is a foreign key |
attribute | description |
---|---|
upper | returns the string in "UPPERCASE" |
lower | returns the string in "lowercase" |
camel | returns the string in "camelCase" |
pascal | returns the string in "PascalCase" |
This list is experimental, and may change in the future. But for now, it is enough to make it work.
And if DBLGen doesn't show anything in the preview window after selecting your template, you probably have a syntax error. I'll improve this error handling in the future.
Although there are tons of other options for ST that I didn't list here, there are some things that won't be able to do in your template easily. For example, when reading data from the database to your object, you may want to call a differnt method for each column, depending on the data type and if the column is nullable or not. For that, you can use Custom Attributes.
Custom Attributes are exactly that, attributes that are not part of the standard list of attributes. You may define custom attributes for tables or columns, as many as you need. You will see a property called "CustomAttributes" on the object details tab, and you can add key/value pairs for each object.
You can then access the attribute value by its key on the template (it cannot conflict with any name already defined). For example, suppose you define that for a column that is an "integer" and accepts null values, you want to define an attribute called "myconverter" that has a method name that would convert that value to a valid C# nullable type (int?). You can then create a key called "myconverter" on the column, and add the value of "MyConverter.ToNullableInt32". On the template, you would use:
_<column.alias.camel> = <column.myconverter>(reader[<column.name>]);that would output something like
_idColumn = MyConverter.ToNullableInt32(reader["IDColumn"]);
Unfortunately, setting custom attributes on each column of every table can be a very long and tedious task. That is where Batch Plugins enter.
Batch Plugins are plugins you can develop in any .NET language to apply custom logic to your objects. They are classes extending "DBLGen.BatchPlugin" and have only one method that will accept a Table object and do whatever you want with them. You can then loop through the columns and add custom attributes to them.
This solution is much better than coding all template logic in a programming language because you can create your plugins to add only small bits of logic, without having to worry with the layout of that on the output file. This makes your plugins really reusable across different types of projects, and really fast to develop if you need to do something quick.
After creating your plugin, compile it to a DLL and drop it in the DBLGen folder. It will load automatically when you open it.