Generated code - Databinding with ASP.NET webforms, SelfServicing

Preface

In ASP.NET web forms, databinding is fully 2-way, and can be setup declaratively, which means you can setup databinding completely in HTML, without the necessity of code in the code-behind file. This section is about databinding with a set of data using a DataSource control.
LLBLGen Pro datasource controls
The LLBLGen Pro runtime framework ships with its own DataSourceControl controls: LLBLGenProDataSource for selfservicing and LLBLGenProDataSource2 for adapter. These also are located in the SD.LLBLGen.Pro.ORMSupportClasses.Web dll. To use them at design time, you have to add them to the toolbox first. This can be done manually by right-clicking the toolbox when a web form is open in the VS.NET editor (HTML or design view) and then by selecting 'Choose items...' which allows you to browse to the SD.LLBLGen.Pro.ORMSupportClasses.Web dll.

Getting started with the LLBLGenProDataSource control

Dragging the datasource control of choice onto a form in design mode will show you the smart tag to configure the datasource. The LLBLGenProDataSource control can be used for an entity collection, TypedList or TypedView. It's recommended you use the smart-tag to setup the LLBLGenProDataSource control. To learn more about the specific properties of the LLBLGenProDataSource control, please consult the LLBLGen Pro reference manual for the LLBLGenProDataSource control.

The LLBLGenProDataSource control accepts a type specification which is used for the particular container type: so the type of the entity collection, the type of a TypedList or the type of a TypedView. The container type, thus what kind of object is contained in the LLBLGenProDataSource control, is specified using the property DataContainerType, accessable in the designer for the LLBLGenProDataSource control and also in the property grid of VS.NET. The contained object itself is exposed through the property belonging to the value of this property: so if the DataContainerType is set to entity collection, an entity collection is inside the LLBLGenProDataSource and the entity collection property is valid, though when DataContainerType is set to TypedList, a TypedList object is contained in the LLBLGenProDataSource control.

The GroupBy property is supported in TypedList/TypedView scenario's while PrefetchPath objects (settable through the PrefetchPathToUse property) are supported in entity collection scenarios.

Caching of data.

The data contained by the LLBLGenProDataSource control is cached in-between post-backs, until the data has to be refreshed.  If caching is enabled (default), the place where this data is cached is either in the ViewState (default), ASP.NET cache or the Session. You can control where the data is cached by setting the LLBLGenProDataSource control's property CacheLocation. You can disable this caching by setting the CacheLocation property to None. If CacheLocation is set to Session, the data is stored in the session object with a key with the following name:

__LLBLGENPRODATASOURCEDATA_controlUniqueID_BindingContainerName

ControlUniqueID is the UniqueID of the DataSource control on the page. BindingContainerName is the name of the container the control is located in. This key is stored in the control state and is always preserved. If the CacheLocation is set to ASPNetCache, the data is stored in the ASP.NET Cache using the following key:

__LLBLGENPRODATASOURCEDATA_Guid

where the Guid is a new Guid per LLBLGenProDataSource control instance. You can control the ASP.NET Cache duration as well by using the property ASPNetCacheDuration which indicates in minutes the time the cached data should stay in the ASP.NET cache. Default is 20 minutes.

Disabling caching, by setting it to DataSourceCacheLocation.None, has the effect that if the LLBLGenProDataSource control is forced to fetch data by a call to ExecuteSelect, it always will refetch the data from the database. This can lead to different data in the page between post-backs (pre post-back and post post-back), so you should be aware of this when using the None setting. As no data is cached, a bound control should use the data in a read-only fashion. When CacheLocation is set to None, the LLBLGenProDataSource control will still cache its own state in the ASP.NET control state.
Setting the data container manually.
The controls offer properties (EntityCollection, TypedList, TypedView) to set the contained object to an external object, for example an entity collection object you've fetched in a method in your business logic tier. This also offers the ability to for example bind myCustomer.Orders to grids through the datasource controls by simply setting the property EntityCollection in the code behind file of the webform. You can do this too with TypedLists by setting the TypedList property and with TypedViews by setting the TypedView property. Be sure to first set the DataContainerType property to the right container type, i.e. EntityCollection, TypedList or TypedView.

Two way databinding.

Two way databinding can be done with the DataContainerType set to EntityCollection. This means the LLBLGenProDataSource control manages not only the binding of data to a control but also the manipulation of data through the bound control (e.g. a GridView) of the data in the contained entity collection. As TypedList and TypedView classes are read-only by definition, you can't manipulate data inside these classes through the LLBLGenProDataSource control. How the LLBLGenProDataSource control fetches data (automatically or through code placed inside an eventhandler) as well as how it saves data (automatically or through event handlers) is discussed in the next section.
LivePersistence and events
A boolean property of the LLBLGenProDataSource control, called LivePersistence, is used to signal the LLBLGenProDataSource control to perform the select, insert, update and delete actions directly on the database (true) or modify the entity collection and add the actions to a UnitOfWork object (false).

If LivePersistence is set to false, fetching data and saving data isn't performed by the LLBLGenProDataSource control but an event is raised instead which passes an event arguments object which contains the parameters for the fetch or save and allows own code to perform the fetch or save. In any case, the changed event is raised so the bound control(s) can refetch the data from the LLBLGenProDataSource control. LivePersistence causes 3 events to be raised:

When a refetch of the data has taken place, the UnitOfWork object contained in the control is cleared. This means that any update/insert/delete work pending has to be completed by that point.

Please examine the events and special event argument classes in the LLBLGen Pro Reference manual.
Refetch
To ensure fresh data from the database. is retrieved, a flag on the LLBLGenProDataSource control called Refetch can be set to true, so the DefaultView will refetch the data, even if for example the page number is the same. This can be necessary if the code-behind code decides the data represented by the LLBLGenProDataSource control is invalidated and has to be refetched from the database.

Using the LLBLGenProDataSource control.

Binding a LLBLGenProDataSource control works like any other web form DataSourceControl: just add the ID as DataSourceID in the bound control's HTML, or select the LLBLGenProDataSource control from the dropdown box for available datasources in the bound control's smart-tag. The designer of the LLBLGenProDataSource control will allow the user to select the DataContainerType and the type of object needed for the container (factory, typedlist or typedview type).

By default no filter is set, no groupby, no prefetch path, no sorting. To set a filter, prefetch path or sort expression, a code behind page is required to set these parameters. This is due to the requirement that these are compile time checked. You can however also produce filters at runtime by using the ASP.NET parameter binding feature. This allows you to setup a binding between a control (or cookie, form etc.) which produces a value and the datasource control so the value produced by the other control is used for filtering. See the example below which uses two drop down boxes to create a filter at runtime for fetching data.

Paging is supported as well: if you want server-side paging, define the paging parameters on the LLBLGenProDataSource2 in the VS.NET property grid. If you want paging inside your bound control (client-side paging), define the paging parameters in the bound control, e.g. the GridView. To be able to work with paging, you of course have to enable paging on the bound control.
The PerformWork event in an AJAX environment
When you use a grid like the DevExpress ASPxGrid for .NET, you can have all edit activities on the client side, using AJAX communication with the server. When you've setup the grid to be used on the client side, and the LLBLGenProDataSource control has LivePersistence set to false, the changes made to the data on the client will be performed in one go when the page gets a post-back. In this scenario, it's more efficient not to bind to PerformWork, but to place a button on the form which simply performs the 'save', e.g. it says "Save changes". By not binding to the PerformWork event, all ExecuteInsert/Update/Delete actions will take place on the data, but aren't propagated to the database just yet, because LivePersistence is set to false. In the button handler of your save button, you then retrieve the UnitOfWork2 object from the LLBLGenProDataSource control from the property UnitOfWorkObject, which contains all changes made and you can commit the changes in one transaction.
Filtering on the fly
The LLBLGenProDataSource control supports filtering on the fly based on parameters specified which can retrieve values from other controls, forms, cookies or other objects supported by the SelectParameters feature of ASP.NET. This is available in a LLBLGenProDataSource control via its SelectParameters property. The SelectParameters property follows the same specification as the ObjectDataSource and are specified declaratively using the <SelectParameters> HTML elements inside a LLBLGenProDataSource control declaration. You can also use the VS.NET designer for setting up the SelectParameters, to do that simply click the [...] button next to SelectParameters in the Property grid of VS.NET. Be sure that the parameter name has the same name as a field in the entity, typed list row or typed view row. You can't use SelectParameters to build a filter on non-entity fields as it builds a filter for the next fetch. 
Trapping invalid input values
As the LLBLGenProDataSource control is the receiver of the data filled into a form, grid or other bound control, it can be these values are invalid, for example they don't match the type of the entity field or they are too big in size. By default, the LLBLGenProDataSource control will ignore these values and won't throw an exception. To make the control throw an exception after all values have been evaluated, you can set the LLBLGenProDataSource property ThrowExceptionOnIllegalFieldInput to true, which signals the control to throw an exception if one or more fields received an illegal value which wasn't convertible to the type of the field in an update/insert scenario. If set to true all illegal values are collected and added to one single ORMValueTypeMismatchException so your code will receive just one exception to handle them all. If set to false, the illegal values are ignored and the fields don't get set to a new value.

Usage examples

Below are two examples: one example using LivePersistence set to true and one using LivePersistence set to false. They're basicly the same form. For VB.NET users: the HTML is for C#, you've to change the first line of the given HTML snippets into the following line to use it with VB.NET:

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %>
Example using LivePersistence
This example contains a form, which filters a list of Order entities, provided by the LLBLGenProDataSource control orderDS, on the OrderEntity field ShippingCountry, provided by a static drop down box, and a drop down box with all customers from Northwind, provided by LLBLGenProDataSource control customerDS. Using parameter binding the two dropdown boxes produce filter information at runtime to produce the proper order list. No code required, it's completely declarative HTML.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="SD.LLBLGen.Pro.ORMSupportClasses.Web" 
    Namespace="SD.LLBLGen.Pro.ORMSupportClasses" TagPrefix="llblgenpro" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        All customers:<br />
        Customers:
        <asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="customerDS" 
            DataTextField="CompanyName" DataValueField="CustomerId" AutoPostBack="True">
        </asp:DropDownList><br />
        ShipCountry:
        <asp:DropDownList ID="DropDownList2" runat="server" AutoPostBack="True">
            <asp:ListItem>Spain</asp:ListItem>
            <asp:ListItem>Germany</asp:ListItem>
        </asp:DropDownList><br />
        <llblgenpro:llblgenprodatasource id="customerDS" runat="server" 
            cachelocation="Session" datacontainertype="EntityCollection" enablepaging="True" 
            livepersistence="True" entitycollectiontypename="NW20.CollectionClasses.CustomerCollection, NW20">
        </llblgenpro:llblgenprodatasource>
        
        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="orderDS" 
            DataKeyNames="OrderId" AllowPaging="True" PageSize="5">
            <Columns>
                <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
                <asp:BoundField DataField="ShipAddress" HeaderText="ShipAddress" SortExpression="ShipAddress" />
                <asp:BoundField DataField="ShipName" HeaderText="ShipName" SortExpression="ShipName" />
                <asp:BoundField DataField="ShipCountry" HeaderText="ShipCountry" SortExpression="ShipCountry" />
                <asp:BoundField DataField="CustomerId" HeaderText="CustomerId" SortExpression="CustomerId" />
                <asp:BoundField DataField="ShipRegion" HeaderText="ShipRegion" SortExpression="ShipRegion" />
                <asp:BoundField DataField="ShipCity" HeaderText="ShipCity" SortExpression="ShipCity" />
                <asp:BoundField DataField="OrderId" HeaderText="OrderId" SortExpression="OrderId" />
                <asp:BoundField DataField="ShipPostalCode" HeaderText="ShipPostalCode" SortExpression="ShipPostalCode" />
            </Columns>
        </asp:GridView>
        <llblgenpro:llblgenprodatasource id="orderDS" runat="server" cachelocation="Session" 
            datacontainertype="EntityCollection" enablepaging="True" 
            entitycollectiontypename="NW20.CollectionClasses.OrderCollection, NW20" livepersistence="True">
            <SelectParameters>
                <asp:ControlParameter ControlID="DropDownList1" Name="CustomerId" 
                    PropertyName="SelectedValue" Type="String" />
                <asp:ControlParameter ControlID="DropDownList2" Name="ShipCountry" 
                    PropertyName="SelectedValue" Type="String" />
            </SelectParameters>
        </llblgenpro:llblgenprodatasource>
    </form>
</body>
</html>
Example using Perform Event handlers
The following example is the same form as in the previous example, with the same functionality, however now it uses LivePersistence set to false, which means we've to perform the persistence logic ourselves by writing event handlers. The HTML is shown first, which is roughly the same as the previous example's HTML except it defines bindings to EventHandlers for PerformGetDbCount, PerformSelect and PerformWork. After that the code behind in VB.NET and C# is shown.

HTML page
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="SD.LLBLGen.Pro.ORMSupportClasses.Web" 
    Namespace="SD.LLBLGen.Pro.ORMSupportClasses" TagPrefix="llblgenpro" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        All customers:<br />
        Customers:
        <asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="customerDS" 
            DataTextField="CompanyName" DataValueField="CustomerId" AutoPostBack="True">
        </asp:DropDownList><br />
        ShipCountry:
        <asp:DropDownList ID="DropDownList2" runat="server" AutoPostBack="True">
            <asp:ListItem>Spain</asp:ListItem>
            <asp:ListItem>Germany</asp:ListItem>
        </asp:DropDownList><br />
        <llblgenpro:llblgenprodatasource id="customerDS" runat="server" 
            cachelocation="Session" datacontainertype="EntityCollection" enablepaging="True" 
            livepersistence="False" entitycollectiontypename="NW20.CollectionClasses.CustomerCollection, NW20" 
            OnPerformSelect="customerDS_PerformSelect">
        </llblgenpro:llblgenprodatasource>
        
        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="orderDS" 
            DataKeyNames="OrderId" AllowPaging="True" PageSize="5">
            <Columns>
                <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
                <asp:BoundField DataField="ShipAddress" HeaderText="ShipAddress" SortExpression="ShipAddress" />
                <asp:BoundField DataField="ShipName" HeaderText="ShipName" SortExpression="ShipName" />
                <asp:BoundField DataField="ShipCountry" HeaderText="ShipCountry" SortExpression="ShipCountry" />
                <asp:BoundField DataField="CustomerId" HeaderText="CustomerId" SortExpression="CustomerId" />
                <asp:BoundField DataField="ShipRegion" HeaderText="ShipRegion" SortExpression="ShipRegion" />
                <asp:BoundField DataField="ShipCity" HeaderText="ShipCity" SortExpression="ShipCity" />
                <asp:BoundField DataField="OrderId" HeaderText="OrderId" SortExpression="OrderId" />
                <asp:BoundField DataField="ShipPostalCode" HeaderText="ShipPostalCode" SortExpression="ShipPostalCode" />
            </Columns>
        </asp:GridView>
        
        <llblgenpro:llblgenprodatasource id="orderDS" runat="server" cachelocation="Session" 
            datacontainertype="EntityCollection" enablepaging="True" 
            entitycollectiontypename="NW20.CollectionClasses.OrderCollection, NW20" livepersistence="False" 
            OnPerformSelect="orderDS_PerformSelect" OnPerformGetDbCount="orderDS_PerformGetDbCount" 
            OnPerformWork="orderDS_PerformWork">
            <SelectParameters>
                <asp:ControlParameter ControlID="DropDownList1" Name="CustomerId" 
                    PropertyName="SelectedValue" Type="String" />
                <asp:ControlParameter ControlID="DropDownList2" Name="ShipCountry" 
                    PropertyName="SelectedValue" Type="String" />
            </SelectParameters>
        </llblgenpro:llblgenprodatasource>
    </form>
</body>
</html>

Code behind

// C#
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

using SD.LLBLGen.Pro.ORMSupportClasses;
using NW20.HelperClasses;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
    }

    protected void customerDS_PerformSelect(object sender, PerformSelectEventArgs e)
    {
        // fetch all customers using the information passed in via the 
        // PerformSelectEventArgs object. This select doesn't have to perform any paging, as the
        // data is for a combo box. 
        e.ContainedCollection.GetMulti(e.Filter, e.MaxNumberOfItemsToReturn, e.Sorter, 
                e.Relations, e.PrefetchPath);
    }

    protected void orderDS_PerformSelect(object sender, PerformSelectEventArgs e)
    {
        // fetch all orders which are in the selected page using the filter passed in
        // via the PerformSelectEventArgs object. 
        e.ContainedCollection.GetMulti(e.Filter, e.MaxNumberOfItemsToReturn, e.Sorter, 
                e.Relations, e.PrefetchPath, e.PageNumber, e.PageSize);
    }

    protected void orderDS_PerformGetDbCount(object sender, PerformGetDbCountEventArgs e)
    {
        // get the total number of orders which match the filter passed in via the
        // PerformGetDbCountEventArgs.
        e.DbCount = e.ContainedCollection.GetDbCount(e.Filter, e.Relations);
    }

    protected void orderDS_PerformWork(object sender, PerformWorkEventArgs e)
    {
        // Perform the work passed in via the PerformWorkEventArgs object. 
        // Start a new transaction with the passed in unit of work.
        using(Transaction trans = new Transaction(IsolationLevel.ReadCommitted, "PerformWork"))
        {
            // pass the transaction to the Commit routine and tell it to autocommit
            // when the work is done. If an exception is thrown, the transaction will
            // be rolled back by the Dispose call of the using statement. 
            e.Uow.Commit(trans, true);
        }
    }
}
' VB.NET
Imports SD.LLBLGen.Pro.ORMSupportClasses
Imports NW20.HelperClasses

Partial Class _Default
    Inherits System.Web.UI.Page

    Protected Sub customerDS_PerformSelect(ByVal sender As Object, ByVal e As PerformSelectEventArgs) _
        Handles customerDS.PerformSelect
        
        ' fetch all customers using the information passed in via the 
        ' PerformSelectEventArgs object. This select doesn't have to perform any paging, as the
        ' data is for a combo box. 
        e.ContainedCollection.GetMulti(e.Filter, e.MaxNumberOfItemsToReturn, e.Sorter, _
                e.Relations, e.PrefetchPath)
    End Sub

    Protected Sub orderDS_PerformGetDbCount(ByVal sender As Object, ByVal e As PerformGetDbCountEventArgs) _
        Handles orderDS.PerformGetDbCount

        ' get the total number of orders which match the filter passed in via the
        ' PerformGetDbCountEventArgs.
        e.DbCount = e.ContainedCollection.GetDbCount(e.Filter, e.Relations)
    End Sub

    Protected Sub orderDS_PerformSelect(ByVal sender As Object, ByVal e As PerformSelectEventArgs) _
        Handles orderDS.PerformSelect

        ' fetch all orders which are in the selected page using the filter passed in
        ' via the PerformSelectEventArgs object. 
        e.ContainedCollection.GetMulti(e.Filter, e.MaxNumberOfItemsToReturn, e.Sorter, _
                e.Relations, e.PrefetchPath, e.PageNumber, e.PageSize)
    End Sub

    Protected Sub orderDS_PerformWork(ByVal sender As Object, ByVal e As PerformWorkEventArgs) _
        Handles orderDS.PerformWork

        ' Perform the work passed in via the PerformWorkEventArgs object. 
        ' Start a new transaction with the passed in unit of work.
        Using trans As New Transaction(IsolationLevel.ReadCommitted, "PerformWork")
            ' pass the transaction to the Commit routine and tell it to autocommit
            ' when the work is done. If an exception is thrown, the transaction will
            ' be rolled back by the Dispose call of the using statement. 
            e.Uow.Commit(trans, True)
        End Using
    End Sub
End Class

Setting values for insert/update using bound parameters

ASP.NET supports bound parameters, where you can define parameters which retrieve the values from other controls, cookies, query string etc. One example is given above, using filtering based on the SelectParameters. The LLBLGenProDataSource control supports also InsertParameters and UpdateParameters. You can define these parameters for insert (saving a new entity) and update (saving a changed entity) resp. the same way as you do with SelectParameters.

This way you can for example set the 'EmployeeId' on a new order entity where you retrieve the EmployeeId from a dropdown control. The value retrieved through a parameter overrules a set value through the bound control. If no value is passed in by the bound control and it's available through the InsertParameters (when inserting) or UpdateParameters (when updating), the value in the Insert/UpdateParameters collection is chosen.

Converting empty string values ("") to NULL values for inserts/updates

In a web-application, form values which are empty are represented as an empty string (""). When an entity is edited through a form, it can be some textboxes or other controls bound to fields of the entity are left empty / point to an empty value: "". The LLBLGenProDataSource2 control will convert "" into NULL for all fields which .NET type isn't the string type. If the field is the string type, this can give a problem: what if the empty string is a valid value?

To tell the LLBLGenProDataSource control that a field should get the empty string as a valid value instead of NULL, you have to pass a List<string> object with all the fieldnames of the fields which should accept "" as the valid value to the property FieldNamesKeepEmptyStringAsValue of the LLBLGenProDataSource control. You should do this in the code behind of your webform.

Example

// C#
// in your Page Load handler routine
if( !Page.IsPostBack )
{
	List<string> fieldsWhichShouldKeepEmptyString = new List<string>();
	fieldsWhichShouldKeepEmptyString.Add( "ShipAddress" );
	_ordersDS.FieldNamesKeepEmptyStringAsValue = fieldsWhichShouldKeepEmptyString;
}
' VB.NET
' in your Page Load handler routine
If Not Page.IsPostBack Then
	Dim fieldsWhichShouldKeepEmptyString As New List(Of String)()
	fieldsWhichShouldKeepEmptyString.Add( "ShipAddress" )
	_ordersDS.FieldNamesKeepEmptyStringAsValue = fieldsWhichShouldKeepEmptyString
End If

This example tells the LLBLGenProDataSource control called '_ordersDS' that the field ShipAddress should get the value "" instead of NULL if the form value is "" for that field.

Normally you don't need to set the property FieldNamesKeepEmptyStringAsValue, if "" is not used for string values and NULL is acceptable instead.

It can be that the list of names which keep the empty string as the value is actually the complete set of fields of the entity. In that case, you can set the property AllFieldsKeepEmptyStringAsValue to true, which makes the LLBLGenProDataSource control to simply not convert empty strings to NULL values for entity fields. Setting this property to true will make LLBLGenProDataSource control to ignore FieldNamesKeepEmptyStringAsValue.

The SortingMode property

The LLBLGenProDataSource control is capable to apply sorting when data has to be fetched, or for example when you've clicked a column header in a bound GridView control. By default, the LLBLGenProDataSource control sorts on the server-side, by producing a SortExpression which is then used by the fetch logic. Which SortExpression is used depends on the value of the property SorterToUse of the LLBLGenProDataSource control and the columns specified by the bound control (e.g. clicked column header). It's possible to tell the LLBLGenProDataSource control to sort on the client-side instead. Do this by setting the LLBLGenProDataSource property SortingMode. By default it's set to ServerSide. If you set it to ClientSide, sorting is applied after the fetch, by sorting the DefaultView object of the datasourcecontrol.

Server-side sorting only uses EntityField objects, so if the entity has a field which isn't a field mapped onto a table/view field, it's ignored in the server-side sorting actions because it's not part of the query send to the database. This is also true for fields mapped onto related fields. In these situations, use client-side sorting.

Be aware that if the CacheLocation is set to None, the LLBLGenProDataSource control always has to fetch the data from the database again, as it can't sort cached data in-memory. If you want to avoid the roundtrip to the database, set the CacheLocation to another value than None.

LLBLGen Pro Runtime Framework v3.5 documentation. ©2012 Solutions Design bv