Generated code - Adding your own code to the generated classes
Preface
Sometimes you want to add your own functionality to the generated code, use it as a base to build upon. Adding your own code to generated
classes has the downside that when you re-generate the code into the same folder, your changes will be overwritten. LLBLGen Pro offers several ways to add your own
code to the generated code to avoid that from happening:
- Through user code regions, which are protected areas in the generated code which contents is preserved when the code is re-generated
- Through include templates, which are templates which are merged at generation time with the standard templates to allow you to merge
your own template code with the standard templates without altering the standard templates
- Through partial classes
This section describes in detail the user code regions and the include templates in greater detail. For include templates it's recommended read the
LLBLGen Pro SDK documentation.
Using partial classes is straight forward and therefore it's discussed only briefly in this section.
We recommend using partial classes to add user code or, if code that is added can be added through automation, use include templates or add your own templates and code generation tasks to the set of tasks executed at code generation time.
User code regions
User code regions are regions which are marked by a start marker and an end marker, both prefixed by the comment operator of the language used
("//" for C# and "'" for VB.NET). The start marker also contains the name of the user code region. The LLBLGen Pro code generator engine is
able to handle multiple user code regions per code file in various scopes. To be able to do this the regions have to be declared in the templates used.
This means that you
can't add your own user code regions to the generated code, though as there are a lot of user code regions defined, in general you
won't run into the need for other user code regions.
The start marker
is
__LLBLGENPRO_USER_CODE_REGION_START. The end marker is
__LLBLGENPRO_USER_CODE_REGION_END. So a typical example of a user code region
is:
// C#
// __LLBLGENPRO_USER_CODE_REGION_START AdditionalNamespaces
// __LLBLGENPRO_USER_CODE_REGION_END
' VB.NET
' __LLBLGENPRO_USER_CODE_REGION_START AdditionalNamespaces
' __LLBLGENPRO_USER_CODE_REGION_END
This user code region has the name
AdditionalNamespaces. To add your own code to a region, place it on lines
between the markers. The markers
have to be on separate lines. So an addition to the example above would be:
// C#
// __LLBLGENPRO_USER_CODE_REGION_START AdditionalNamespaces
using System.Data;
// __LLBLGENPRO_USER_CODE_REGION_END
' VB.NET
' __LLBLGENPRO_USER_CODE_REGION_START AdditionalNamespaces
Imports System.Data
' __LLBLGENPRO_USER_CODE_REGION_END
The code generator engines for TDL (CodeEmitter) and .lpt templates both can handle user code regions so the concepts to use them are the same.
Both code generator engines use the user code region name to find back a region in an existing file with the same name.
This means that if you generate code into an empty folder each time you generate code, your additions are not preserved as the code generator
can't read the current code file and can't find your additions which have to be preserved.
Note: |
If you're adding custom code to user code regions which are inside Init methods and which receive an EntityFields(2) object, be sure to first test the State property of that fields object to see if it's equal to EntityState.New. If not, e.g. it's EntityState.Fetched, you shouldn't set any field values to initialize these fields, because the passed in EntityFields(2) object is already filled with data from the database. Setting fields to a value in that particular situation will overwrite the values from the database, which could lead to unpredictable results. |
Partial classes
LLBLGen Pro generates all classes generated for the LLBLGen Pro runtime
framework as partial classes.
For VB.NET this doesn't make a difference as
any class can be extended by another code file with a partial class definition with the same name, though in C#, all classes are generated as partial classes.
To extend any class in the generated code, just define in the same
namespace and
assembly a new definition of an existing class and specify the keyword
partial (C#) or
Partial (VB.NET). For example, consider an entity
CustomerEntity being generated by LLBLGen Pro. You can then extend this class
by adding a new file to the generated VS.NET project and add the following code to that file: (the rootnamespace used for this project was 'Northwind')
// C#
namespace Northwind.EntityClasses
{
public partial class CustomerEntity
{
// your code here
}
}
' VB.NET
Namespace Northwind.EntityClasses
Public Partial Class CustomerEntity
' your code here
End Class
End Namespace
You can then add your code to this class which will then be compiled into the class with the same name in the same project by VS.NET. Be aware that you're not
inheriting a class, so you can't specify a method or property with a name already in the generated code's class
Include templates
Note:
|
Include templates is an advanced topic for which it's recommended you've read the the LLBLGen Pro SDK (available to customers) documentation.
It is possible to get started without the SDK, but it is recommended to consult the SDK for further details.
|
LLBLGen Pro's code generator engines can handle include templates, which means that in a template, a template include statement can be added and the
contents of the template bound to the template id specified in the template include statement will replace the template include statement. This is similar
to the well known include tags for ASP pages. The includes can be used recursively, which means an included template can again contain include statements.
At various places in the templates of the SelfServicing and Adapter template groups, include statements are added. Furthermore, several templates have been
broken into several templates to make it easier to adjust the generated code through include templates.
As include templates work with template id's, using a
template bindings file which binds a different code file to a template id can make the generated
code look different: your template will be included into the standard template and executed together with the standard template. Please also see
Concepts - Templates and Template groups
and the LLBLGen Pro Designer documentation for more information about templatebindings files and how to use them in the designer, for
example how to make your template overrule a standard template.
To easily create templatebindings files, please use the templateBindingsDefinition.xsd file in the Tasks\XSDs folder in the LLBLGen Pro installation folder.
If you first open the .xsd in VS.NET and then your templatebindings file, you have intellisense on the xml you write in your templatebindings file.
Below is a list of template id's you can add to your own templatebindings file and which will make the contents of the file bound to the
template id be inserted into the template the template is included in. There are more template ids defined for this purpose, though beginners should
start with this list. The full list is available in the SDK documentation.
TemplateID |
Description |
Included in |
Custom_CommonEntityBaseTemplate |
Custom include template ID for the template which is used for the CommonEntityBase class, which is the class sitting in between the EntityBase(2) classes and the generated entity classes. |
The CommonEntityBase class. |
Custom_ConstantsEnumsTemplate |
Custom include template ID for the template which is used for the ConstantsEnums codefile. |
The Root namespace specification of SD_ConstantsEnumsTemplate |
Custom_DataAccessAdapterTemplate |
Custom include template ID for the general data access adapter object. Database specific. Adapter specific. |
The DataAccessAdapter class |
Custom_CommonDAOBaseTemplate |
Custom include template ID for the template which is used for the CommonDaoBase helper class. |
The CommonDaoBase class |
Custom_EntityAdapterTemplate |
Custom include template ID for entity classes. Adapter specific. |
The [EntityName]Entity classes. |
Custom_EntityBaseTemplate |
Custom include template ID for the template which is used for the entity base classes in a scenario where two classes per entity are generated. |
The [EntityName]EntityBase classes |
Custom_EntityCollectionAdapterTemplate |
Custom include template ID for the general collection class for all entities. Adapter specific |
The EntityCollection class |
Custom_EntityCollectionTemplate |
Custom include template ID for the template which is used for the Entity Collection classes. |
The [EntityName]Collection classes |
Custom_EntityDAOTemplate |
Custom include template ID for the template which is used for the Dao classes in selfservicing. |
The [EntityName]DAO classes |
Custom_EntityInitializationTemplate |
Custom include template ID which is used for templates which are inserted into the InitClass* methods of an entity, at
the end of the init class method. |
All entity classes (selfservicing/adapter) |
Custom_EntityTemplate |
Custom include template ID for the template which is used for the Entity classes. |
The [EntityName]Entity classes. |
Custom_EntityUsingEntityBaseTemplate |
Custom include template ID for the template which is used for the entity classes in a scenario where two classes per entity are generated. |
The [EntityName]Entity classes. |
Custom_EntityValidatorTemplate |
Custom include template ID for the template which is used for the Validator classes per entity, used for per field validation.
This template is included in the class, not in the method. For injection of custom validation code into the Validate method, bind
SD_EntityValidatorIncludeTemplate to your custom template. |
The [EntityName]Validator classes. |
Custom_ResultsetFieldsAdapterTemplate |
Custom include template ID for the resultsets class, used in typed lists. Adapter specific. |
The ResultsetFields class |
Custom_ResultsetFieldsTemplate |
Custom include template ID for the template which is used for the ResultsetFields helper class. |
The ResultsetFields class |
Custom_TypedListAdapterTemplate |
Custom include template ID for the typed list classes. Adapter specific. |
The [TypedListName]TypedList class. No include is specified for the [TypedListName]TypedListRow class. |
Custom_TypedListDAOTemplate |
Custom include template ID for the template which is used for the TypedList DAO class, which is also used by TypedViews. |
The TypedListDAO class |
Custom_TypedListTemplate |
Custom include template ID for the template which is used for the TypedList classes. |
The [TypedListName]TypedList class. No include is specified for the [TypedListName]TypedListRow class. |
Custom_TypedViewAdapterTemplate |
Custom include template ID for the TypedView classes. Adapter specific. |
The [TypedViewName]TypedView class. No include is specified for the [TypedViewName]TypedViewRow class. |
Custom_TypedViewTemplate |
Custom include template ID for the template which is used for the TypedView classes. |
The [TypedViewName]TypedView class. No include is specified for the [TypedViewName]TypedViewRow class. |
A good place to start is adding a template binding to Custom_EntityInitializationTemplate. The contents of the template bound to that template id
will then be inserted into the InitClass methods of the entity classes. This template can for example be used to set the ConcurrencyPredicateFactoryToUse
property of every entity to an instance of a generic IConcurrencyPredicateFactory implementation.
As it will be code which will be generated into the generated code, you don't have to worry about losing your additions: they're always there. Keep your
include templates with your .lgp project file so you'll always have them available with the .lgp project file. You can specify in the .lgp file an additional
Templates folder where you can store the templatebindings files and templates you've created.
Include templates can be either written using Template Definition Language (TDL, which is LLBLGenPro's native template language) or using C# or VB.NET
in .lpt templates (which use the <% %> syntaxis for code blocks, similar to asp syntaxis).
The tutorial below is a small tutorial to write an include template using a simple text editor or VS.NET, as is used below.
Adding code to an entity
We will first create a new templatebindings file for our template. The template file and the templatebindings file are stored in the default
folder for templates:
LLBLGen Pro installation folder\Templates.
- In the LLBLGen Pro designer, open the Template Bindings Viewer and
create a new template bindings file. Please consult the Designer
documentation, 'How to ... work with templates... to add a new template'
for details.
- If you open the new Templatebindings file as xml inside the
designer, you should change it to look like this:
<?xml version="1.0"?>
<templateBindings xmlns="http://sd/llblgen/pro/templateBindingsDefinition.xsd" name="My template bindings"
description ="My include template bindings"
precedenceLevel="11">
<supportedFrameworks>
<framework name="LLBLGen Pro Runtime Framework"/>
</supportedFrameworks>
<supportedPlatforms>
<platform name=".NET 3.5"/>
<platform name=".NET 4.0"/>
<platform name=".NET 4.5"/>
</supportedPlatforms>
<language name="C#">
<templateBinding templateID="Custom_EntityUsingEntityBaseTemplate" filename="customEntityCodeInclude.template" />
</language>
<language name="VB.NET">
<!-- If you're using VB.NET, you should add the binding above here instead. -->
</language>
</templateBindings>
- Verify that everything is as you want it to be, and then save MyBindings.templatebindings.
Save it in a folder reachable by the designer for templates, so either
in the Templates folder of the LLBLGen Pro framework or in the folder
specified as 'AdditionalTemplatesFolder' in the project settings.
- Click the 'Refresh the code generation meta-data' button in the
toolbar or select the option from the Tools menu. The template bindings
file should now be available in the templatebindings selector in the
Template Bindings Viewer in the designer.
After creating the templatebindings file, we're now set to write the actual template so we can use it in the code generation process. The example above
bound the template id
Custom_EntityUsingEntityBaseTemplate to the template we will write in next steps. This means it will be used in
SelfServicing TwoClasses scenario's. If you're using
Adapter, you should specify
Custom_EntityAdapterTemplate instead in the
templatebindings above.
Writing the actual template takes a few steps. You can use any text editor you want.
Using .lpt templates as include templates
As discussed briefly in the previous section, LLBLGen Pro supports two kinds of templates: TDL templates and .lpt templates. The .lpt templates are templates
which have the template code in <% %> brackets, similar to various code generators out there, and the template code is able to access every element
in the LLBLGen Pro project object graph. You can bind a .lpt template to a template ID which is used for including another template,
so you can use .lpt templates as include templates. TDL templates are interpreted, .lpt templates are compiled into an assembly, though this is handled by the
TDL interpreter for you and it integrates transparently.
As discussed in the LLBLGen Pro SDK, each .lpt template results in a class which implement ITemplateClass. In your template you have acces to three main
parameters:
- _executingGenerator, which is of type IGenerator, your gateway to the Project object and more. IGenerator is defined in the
SD.LLBLGen.Pro.ApplicationCore assembly of the LLBLGen Pro designer.
- _parameters, which is a hashtable with all the parameters defined for the task currently executing, stored with the parameter name as the key.
- _activeObject, of type object
In your .lpt template you can access these objects and the data they contain in your script blocks. We'll see how this works by rewriting the
previous example in an .lpt include template below. When a .lpt template is used as an include template, the TDL interpreter will pass on its complete state
to the .lpt template in the _activeObject, which will represent a Hashtable. The following list shows which object is stored
under which key in the _activeObject's Hashtable. It contains the current scope of the TDL interpreter at the point when the template include statement was
found:
- CurrentEntity, the currently set currentEntity
- CurrentRelatedEntity, the currently set related entity
- CurrentFieldOnRelatedField, the currently set currentfieldOnRelatedField
- CurrentEntityField, the currently set currentEntityField
- CurrentRelatedEntityField, the currently set currentRelatedEntityField
- CurrentEntityFieldRelation, the currently set currentEntityFieldRelation
- CurrentEntityRelation, the currently set currentEntityRelation
- CurrentTypedListRelation, the currently set currentTypedListRelation
- CurrentTypedList, the currently set currenttypedList
- CurrentTypedView, the currently set currenttypedview
- CurrentTypedListField, the currently set currentTypedlistfield
- CurrentSPCall, the currently set currentSPCall
- CurrentSPCallParameter, the currently set currentSPCallParameter
- CurrentTypedViewField, the currently set currentTypedViewField
Keys are strings, values are objects or null if not set.
With this knowledge we can rewrite previous section's tutorial a little to use a .lpt include template instead:
You should then again generate code as previously stated. It should give you the same code as before.