I just don't get it (templating system)

Posts   
 
    
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 07-Apr-2012 16:01:14   

Warning...long ramble ahead...

I want to generate some additional metadata to go with any generated entities/typedlists/typedviews.

Now I've done all this before (long ago) and have the 3 old templates so I simply renamed them remove to 'Adapter' from their filenames. I also renamed the old templatebindings file I had and modified it to reflect the new template filenames and changed the templateID names too. I saved them together in a Templates folder.

That bit is easy, now to wade through the docs and find out how to actually use them in the build. I find it very complicated as I see and think I understand most of the bits but when it comes to actually putting them in I'm stuck again. However, spurred on by my previous experience that once it is in and configured, 'it just works' I did the following:

  • Did a 'Save As' in 'Task queue to execute' and made a copy of SD.Presets.SelfServicing.General2010 just replacing the 'SD' with 'My' and saved it in my local Tasks folder.
  • Remembered to add the two folders into the appropriate places in Preferences.

Now I need to get LLBLGen to use my new templates(IDs) and I am stuck: The Template Bindings Viewer shows the contents of my templatebindings file and I can see the three new TemplateIDs are correctly mapped to my template files but where do I tell it to use those TemplateIDs. Is it a Task? Do I need to add a Task into my new presets file? Which existing Task should I copy and tweak? I don't know so I read all the docs, including the SDK, again and can't make the connection linking templates to task to generated code.

(Whilst doing this, I think I have found a little bug in the Template Bindings Viewer: If I set the Language filter to C#, I can hide all those horrid VB.Net bindings smile but when you change to another TemplateBinding, they all come back again - you have to reapply the filter)

I also wonder what the Include Only checkbox does for the templateIDs. Don't know and I make a mental note to look into it once I have things working.

I look at the 'Task queue to execute' and find all 'SD' prefixed tasks. Ah! I see an Add Tasks button and think that must be what I need! Nope, it just lists the same items that are already in the Run Queue plus maybe a few more?? Its hard to compare when there are that many items all with similar names.

Not sure what to do now. I think I'll have a look at the code I generated earlier because 1) I need to know where I want to place my new metadata if ever I get it working (maybe a partial class but which folder?); 2) If I can find some specific text in the generated code, maybe I can find it in the SD templates and work backwards to see the TemplateID that it represents and then how and where that TemplateID got used.

So I had a look at one generated entity and got quite a shock when I found that my metadata was already there!frowning frowning frowning That's it I thought. Frans has added ESP to LLBLGen and thats the end of software development as we know it. smile

Rationalisation took over. Maybe LLBLGen v3.x just happened to create the same metadata I wanted anyway. I changed a comment in my template and regenerated....it appeared as if by magic.

So the only explanation I have is that a TemplateID is there to allow for an 'override' by mapping a different filename/precedence but if only one is found it will just get used - there is no need to create a task - this is a concept that I still don't 'get' from the docs.

I think I have the following questions: - The generated code appeared in the Include region within the entity (and presumably typedlist/typedview classes if I had any in this test project). Why? How is a TDL template file linked with an output type and where should I read up on this in the docs. - What does Include Only mean in the designer - How can I create a folder called 'XYX' and create empty partial classes for all the entities. No wait, don't answer that - I'll just close my eyes, concentrate hard and it will just happen.smile

Cheers Simon

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 09-Apr-2012 00:22:17   

Hi Simon,

Very entertaining post to read simple_smile

I will assume you are using v3.x, however the template system per-se (templatebindins, tasks, presets) did't change very much over major versions, just changed a bit the TDL/LPT languages.

Before answer your questions I will try to dig into how the template system works. First let simply clarify some concepts:

**Templates **(.template/.lpt). Those are the physical files you wrote which are written in either TDL or LPT languages. In simple words, templates are the actual code to be parsed/compiled/generated.

**TemplateBindings **(.templatebindings). A template binding file is the one that puts names to your templates files. So it says: The template located at \CustomStuff\Net2.x\C#\AdditionalMetadata.template be named MY_CustomStuff, that's it. Additionally you could specify which languages/frameworks supports that templates (the group of templates included in the bindings file). A typical representation of this would be:

<?xml version="1.0" encoding="utf-8"?>
<templateBindings name="MY.AdditionalTemplates.CustomStuff.Net2.0" description="MyCustomStuff.. blah blah" xmlns="http://sd/llblgen/pro/templateBindingsDefinition.xsd">
    <supportedPlatforms>
        <platform name=".NET 2.0" />
        <platform name=".NET 3.0"/>
        <platform name=".NET 3.5"/>
    </supportedPlatforms>
    <language name="C#">
        <templateBinding templateID="MY_CustomStuff" filename="CustomStuff\Net2.x\C#\AdditionalMetadata.template" templateLanguage="C#" />
    </language>
</templateBindings>

In simple words, templateBindings tells LLBLGen how to find a template.

**Tasks **(.tasks). Now, you have all your templates bound to templatesIDs and filtered by framework/platform/language. But LLBLGen stills doesn't know what to do with them. That is because a template could be treated (compiled, parsed, etc) in different ways. What tells LLBLGen what to do with templates are the Tasks files. Each task has a TaskPerformer assigned, which could be DirectoryCreator, DotNetTemplateEngine, CodeEmitter, ProjectFileCreator, or even (why not) your own task performer implementation. So a task takes a TaskPerformer and a Template and do the hard work, which is actually parse/compile/etc a template and generate some code based on it. Each TaskPerformer has some parameters that indicate specifically how the work has to be done on that template, for instance whether you want to execute the taskPerformer on each entity/typedView/etc or just execute it once, the file extension to be generated, the output folder, etc. In simple words, Tasks files tells LLBLGen what template to process and how to process it.

**Presets **(.presets). Now you have the code to be generated, also you have the file that tells how to find those templates, and you have the file that tell LLBLGen how to process them. It seems that all ends here. But no, because you don't want to execute always all tasks, and maybe you don't want to execute tasks with the default parameters, or maybe you want to execute tasks from one T1.tasks file along with other T2.tasks file together in one generation process. Here is when .presets are needed. A preset is a file that tells LLBLGen what tasks to execute, in what order, and what parameters to override over the original task. What you see in the "Task queue to execute" is actually a preset, where you can add/remove/re-order tasks and save that configuration in a new preset file. The preset is what average users actually "see" and can play with. In simple words, a Preset is a tasks generation config file, where you say to LLBLGen what to generate now.

All this is in the SDK docs, but you already read it, so I think it's a matter of get used to the concepts and practice. I know it seems like a lot of files and concepts, but it makes the generation process very powerful and flexible.

I will answers your other questions in the next post...

David Elizondo | LLBLGen Support Team
daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 09-Apr-2012 00:40:37   

... ok, now your questions:

simmotech wrote:

  • The generated code appeared in the Include region within the entity (and presumably typedlist/typedview classes if I had any in this test project). Why? How is a TDL template file linked with an output type and where should I read up on this in the docs.

See your .templatebindings file. I think you named the template Custom_EntityDAOTemplate or SD_EntityIncludeAdapterTemplate. If you open the SD_EntityDAOTemplate you will see a pre-processor include statement at the end: <# Custom_EntityDAOTemplate #>. That means that if LLBLGen can found a template with that name, it will expand it exactly there. This is explained in detail in Adding your own code to the generated classes - Include templates. Also in the SDK docs, Template pre-processor and includes.

simmotech wrote:

  • What does Include Only mean in the designer

I quote the docs: The attribute _**includeOnly **_is optional, and can be used to specify that the template is used as include template only. This is required for .lpt include templates, as otherwise the template will be seen as a normal template and a separate code producer class is created for the template, which likely will cause compile errors as the template isn't a full template.

simmotech wrote:

  • How can I create a folder called 'XYX' and create empty partial classes for all the entities. No wait, don't answer that - I'll just close my eyes, concentrate hard and it will just happen.

Ok wink good luck! ... just kidding. For the folder thing, you need a task that uses the DirectoryCreator performer. See the step (3) in the following point:

For the empty partial classes, follow these steps:

  1. Create your template file and save it (i.e. AdditionalTemplates\CustomStuff\entityPartial.template). A template file that is the code to be generated. This is what your partial class will look like. Example:
using System;
// other desired usings...
using <[RootNamespace]>;
using <[RootNamespace]>.FactoryClasses;
using <[RootNamespace]>.DaoClasses;
using <[RootNamespace]>.RelationClasses;
using <[RootNamespace]>.HelperClasses;
using <[RootNamespace]>.CollectionClasses;
<[Foreach AdditionalNamespace CrLf]>using <[CurrentAdditionalNamespace]>;<[NextForeach]>
using SD.LLBLGen.Pro.ORMSupportClasses;

namespace <[RootNamespace]>.EntityClasses
{   
    public partial class <[CurrentEntityName]>Entity        
    {
        // put your custom partial class code here...
    }
}
  1. Create the templateBinding file which tells how to find your template and save it (i.e. : \AdditionalTemplates\My.TemplateBindings.CustomStuff.NET20.templatebindings).
<?xml version="1.0"?>
<templateBindings xmlns="http://sd/llblgen/pro/templateBindingsDefinition.xsd" name="MY.TemplateBindings.CustomStuff.NET20"
                  description ="Custom Stuff, partial classes, etc. Specific for .NET 2.0/3.0/3.5/4.0 based platforms"
                  precedenceLevel="10">
    <supportedPlatforms>
        <platform name=".NET 2.0"/>
        <platform name=".NET 3.0"/>
        <platform name=".NET 3.5"/>
        <platform name=".NET 4.0"/>
    </supportedPlatforms>
    <supportedFrameworks>
        <framework name="LLBLGen Pro Runtime Framework"/>
    </supportedFrameworks>

    <language name="C#">
        <templateBinding templateID="MY_PartialClassTemplate" filename="CustomStuff\entityPartial.template" templateLanguage="TDL" />       
    </language>
</templateBindings>
  1. Create the tasks file, which tells LLBLGen that you want a folder, and also you want to execute the code generator for each entity, using the MY_PartialClassTemplate. Then save it (i.e. AdditionalTasks\MY.Tasks.CustomStuff.tasks).
<?xml version="1.0"?>
<taskGroup xmlns="http://sd/llblgen/pro/taskGroupElementDefinitions.xsd"
        name="MY.Tasks.CustomStuff" isOptional ="true" 
        description="General group of tasks which are used by all template groups.">

    <supportedPlatforms/>
    <supportedTemplateGroups/>
    <supportedFrameworks>
        <framework name="LLBLGen Pro Runtime Framework"/>
    </supportedFrameworks>
    <dependencies/>
    <taskGroup name="MY.Tasks.CustomStuff.DirectoryCreators" description="Tasks which create various directories" isOptional="true">
        <task name="MY.Tasks.CustomStuff.PartialClassesDirectoryCreator" assemblyFilename="SD.LLBLGen.Pro.TaskPerformers.dll" 
           taskPerformerClass="SD.LLBLGen.Pro.TaskPerformers.DirectoryCreator" isOptional ="true" description ="Creates the Entity Partial classes folder">
            <supportedPlatforms/>
            <supportedTemplateGroups/>
            <supportedFrameworks>
                <framework name="LLBLGen Pro Runtime Framework"/>
            </supportedFrameworks>
            <dependencies/>
            <parameters>
                <parameter name="folderToCreate" defaultValue="EntityPartialClasses" isOptional="false" description="The folder to create"/>
                <parameter name="failWhenExistent" defaultValue="false" isOptional="true" description="Flag to signal what to do when the folder already exists. Overrules clearWhenExistent" valueType="boolean"/>
                <parameter name="clearWhenExistent" defaultValue="false" isOptional="true" description="Flag to signal if an existing folder has to be cleared first. Overruled by failWhenExistent" valueType="boolean"/>
            </parameters>
        </task>
    </taskGroup>
    <taskGroup name="SD.Tasks.CustomStuff.FileCreators" description="Tasks which create various files" isOptional="true">
        <task name="SD.Tasks.CustomStuff.EntityPartialClassFileGenerator" assemblyFilename="SD.LLBLGen.Pro.TaskPerformers.dll" 
           taskPerformerClass="SD.LLBLGen.Pro.TaskPerformers.CodeEmitter" description="Generates the entity partial classes" isOptional="true">
            <supportedPlatforms/>
            <supportedTemplateGroups/>
            <supportedFrameworks>
                <framework name="LLBLGen Pro Runtime Framework"/>
            </supportedFrameworks>
            <dependencies>
                <dependency name="MY.Tasks.CustomStuff.PartialClassesDirectoryCreator"/>
            </dependencies>
            <parameters>
                <parameter name="destinationFolder" defaultValue="EntityPartialClasses" isOptional="false" description="The folder to generate the code in"/>
                <parameter name="filenameFormat" defaultValue="[elementName].[extension]" isOptional="false" description="The destination file format specification"/>
                <parameter name="templateID" defaultValue="MY_PartialClassTemplate" isOptional="false" description="The ID of the template to use." valueType="templateID"/>
                <parameter name="emitType" defaultValue="allEntities" isOptional="false" description="The type of code generation to perform." valueType="emitType"/>
                <parameter name="templateBindingDefinitionName" defaultValue="" isOptional="true" description="The name of the TemplateBindings from which to pick the templateID specified. Specifying this parameter will always force the templateID to be picked from the templateBindings with the name specified."/>
                <parameter name="failWhenExistent" defaultValue="false" isOptional="true" description="Flag to signal what to do when the destination file already exists." valueType="boolean"/>
            </parameters>
        </task>     
    </taskGroup>
</taskGroup>
  1. You are almost done. Now you just need to create a preset. You can do this in the Designer in "Task queue to execute". Click 'new' put it a name and save it in your AdditionalTasks folder and add the tasks you just created. You also can create it manually:
<?xml version="1.0" encoding="utf-8"?>
<preset name="MY.Presets.CustomStuff" lastModifiedOn="2012-04-08T16:15:46.9491757-06:00" createdBy="" isSealed="false" xmlns="http://sd/llblgen/pro/presetElementDefinitions.xsd"> 
  <supportedFrameworks>
    <framework name="LLBLGen Pro Runtime Framework" />
  </supportedFrameworks>
  <taskPresets>
    <taskGroupPreset name="MY.Tasks.CustomStuff" requiresCleanCache="false">
      <taskGroupPreset name="MY.Tasks.CustomStuff.DirectoryCreators" requiresCleanCache="false">
        <taskPreset name="MY.Tasks.CustomStuff.PartialClassesDirectoryCreator" />
      </taskGroupPreset>
      <taskGroupPreset name="SD.Tasks.CustomStuff.FileCreators" requiresCleanCache="false">
        <taskPreset name="SD.Tasks.CustomStuff.EntityPartialClassFileGenerator" />
      </taskGroupPreset>
    </taskGroupPreset>
  </taskPresets>
</preset>

There are a lot of examples if you examine the shipped templates. Hope helpful wink

David Elizondo | LLBLGen Support Team
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 09-Apr-2012 08:30:10   

Thanks David!

So the TemplateIDs I created were coincidentally (from my point of view) the correct names to be used as an include to the main templates.

I promise I will read the docs more carefully next time.

Cheers Simon