Concepts - Templates and Template groups
Included template files
The
LLBLGen Pro Framework ships with two
Template groups of template files: "SelfServicing"
and "Adapter".
Each template group generates code which is
different in style, functionality and paradigm from the other. Which template group to use is selected by the template group combo box
in the generator configuration window in the LLBLGen Pro designer when you
generate code. Per template group you can have different scenario's, like 'General' and 'Two classes', which can be
selected by picking the right preset.
Below are short descriptions of the template groups' typical aspects, which can
help you decide which template group to choose for your project.
SelfServicing
SelfServicing is the template group which was initially shipped with LLBLGen Pro and it's named
SelfServicing because the entity,
typed list and typed view classes contain all the logic and data to help themselves interacting with the persistent storage. This means that
there is no extra object instance required, to fetch entity data from the persistent storage or save data into the
persistent storage. This can be very handy if you don't want to be bothered with the hassle of extra objects to fetch data related to
an instance you hold or you want to traverse relations like myCustomer.Orders[0].Products[0].ShipperName. This traversal fetches
data on the fly with
lazy loading (load on demand) logic that is controlled by code inside the entities itself. This is transparent for the developer
and is easy to use.
The presence of persistence logic inside the entity, typed list and typed view classes can in some situations be a disadvantage of the SelfServicing code.
For example if you as a project manager do not want to have your GUI developers call myEntity.Save(), you have to add extra code
to the generated classes to prevent any save action from happening and even then, the logic is exposed to the developer. The template group called
Adapter
solves this (see below). In short, SelfServicing is about the paradigm that object persistence is something about the objects being persisted, so an
entity object should know how to persist itself.
An example piece of SelfServicing code is the following. This code reads an Order entity from the persistent storage, alters its
EmployeeID field and saves it back.
// C#
OrderEntity myOrder = new OrderEntity(10254);
myOrder.EmployeeID = 6;
myOrder.Save();
' VB.NET
Dim myOrder As New OrderEntity(10254)
myOrder.EmployeeID = 6
myOrder.Save()
SelfServicing comes in two variants:
General and
TwoClasses. The General variant, formulated in the
General preset generates one class per entity. The
TwoClasses variant
generates two classes per entity:
entityNameEntityBase, which contains the generated plumbing code and
entityNameEntity, which derives from
that class and which is for your business logic.
SelfServicing code is for one database type
only. You can't re-use an entity class on multiple database types (e.g. SQL
Server and Oracle). If you need to persist an entity into multiple database
types, use Adapter.
Adapter
Adapter is called
Adapter because all persistence actions are performed by an 'Adapter' object, similar to the DataAdapter in .NET.
Adapter's entities don't have persistence information, nor do they
contain persistence action logic. Because it uses adapter objects, called
DataAccessAdapter, you can choose the adapter you want to use
to persist the entity object when you actually need it.
Adapter's generated code is not 'self-serving', which means that you have to pull data from the database up front or consult a DataAccessAdapter
object to read data, as there is no logic inside the entity objects to control persistence actions like reading related entities from
the database through
lazy loading. This can be perfect for projects where it is mandatory that every database action is started and controlled by a
specific piece of code, e.g. a selected set of Business Logic classes in a middle tier. It can however be a bit cumbersome for projects where self-servicing aspects of an entity should be utilized. It is therefore important to understand how you look at what persistence of
objects means to your code.
Adapter is about the paradigm that persistence is a 'service'
provided by an object or group of objects and which is provided by logic outside the objects affected by the service. As an illustration
of this, the same functionality provided as an example with the SelfServicing explanation above is written using the Adapter logic:
// C#
OrderEntity myOrder = new OrderEntity(10254);
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
adapter.FetchEntity(myOrder);
myOrder.EmployeeID = 6;
adapter.Save(myOrder);
}
' VB.NET
Dim myOrder As New OrderEntity(10254)
Using adapter As New DataAccessAdapter()
adapter.FetchEntity(myOrder)
myOrder.EmployeeID = 6
adapter.Save(myOrder)
End Using
Adapter requires more lines of code when fetching data, however it also gives more control over the steps taken, for example it allows you to specify whether the
connection should stay open so actions taken after the current action will be faster, because an open connection is already available
(the connection is closed otherwise after each action), or which connection string to use for this particular action. It is therefore key to first determine
what kind of paradigm you want to use before generating code.
Adapter also comes in two variants:
General and
TwoClasses. The
General variant, formulated in the
General preset generates one class per entity. The
TwoClasses variant
generates two classes per entity:
entityNameEntity, which contains the generated plumbing code and My
entityNameEntity,
which derives from that class and which is for your business logic.
The TwoClasses variant was added when .NET didn't support partial classes.
As all classes generated are partial classes, it's recommended to use
General variant and add code to the generated classes by using partial
classes
.
All template groups are fully supported and have the same basic persistence options, however one template group can have more sophisticated persistence options
than the other while the other template group might have more functionality on board for ease of use.
When to use which template group?
Both template groups offer a wide set of functionality, but do that in a different way. This difference makes it important for you to know when to pick
which template group for a given project. Below are two short lists which sum up some reasons for choosing each template group and which will help
you with the decision which template group to take.
If you really don't know what to pick, choose Adapter as it offers the most
flexibility, although it doesn't support Lazy Loading. Lazy Loading is a
neat feature but not a necessity to for a proper application.
Both template groups are supported in WCF Data Services and WCF Ria
Services.
In general we recommend Adapter, as it's the most flexible.
When to use SelfServicing
- When the project targets a single database type.
- When you are not using a distributed scenario like remoting, webservices, WebAPI or WCF services.
- When you need navigation through your object model in a databinding scenario and you want to load the data using lazy loading.
- When you don't need to extend the framework.
- When you like to have the persistence logic into the entity classes.
- When you do not require fine grained database access control, like targeting another database
per call.
When to use Adapter
- When the project targets multiple database types or you want be able to do that in the future.
- When you are using a distributed scenario like remoting, webservices, WebAPI or WCF services.
- When you don't need lazy loading scenarios in relation traversals. You can still navigate relations but you have to fetch the data up-front.
- When you need to extend the framework with derived classes.
- When you like to see persistence logic as a 'service', which is offered by an external object (session/context, adapter).
- When you require fine grained database access control.
- When you want to convert Entities to XML and back and performance and XML size are important.