Getting started
LLBLGen Pro ships with a full Linq provider. Full linq support means that any querying on LLBLGen Pro generated entities can be done through Linq constructs/queries, which makes it an alternative to LLBLGen Pro's own QuerySpec API or the more low-level querying API with predicates, relations and the like. It's still recommended to read and learn about the LLBLGen Pro querying API elements as discussed in Filtering and Sorting so the terms used in this section are familiar to you. LLBLGen Pro's Linq support builds on the existing querying API with predicates, relations, prefetch paths and the like.
This section assumes you're familiar with what Linq is. When Linq is mentioned in this section, the language extensions to C# and VB.NET are meant, not Linq to Sql or Linq to Objects or other framework. To get yourself familiar with Linq we recommend to read the MSDN documentation for .NET / Visual Studio about Linq features in C# or VB.NET. We like to refer to our Linq support as Linq to LLBLGen Pro, which is in-line with the terminology Microsoft uses for the various Linq support flavors they've released.
We tried our best to implement support for as much Queryable extension methods and as much Linq constructs as possible. This documentation won't repeat every single extension method of Queryable, only the ones which require your attention or which aren't supported.
Linq to LLBLGen Pro uses the SQL truth tables of your database when it comes to comparing with NULL, and not the truth tables
of comparing with null
/Nothing
of the .NET language you're using. As this might differ per database, you have to consult
your database's manual for what it will return if e.g. a field is compared with NULL or a field A is compared with a field B and one
or both are NULL.
To get started with Linq to LLBLGen Pro, your project should reference the generated code project(s) and the SD.LLBLGen.Pro.ORMSupportClasses.dll. You can then develop Linq queries using your LLBLGen Pro generated entities inside your code. If you're using Adapter, you've got to pass a valid DataAccessAdapter instance to the Linq query before it's executed. More on this below.
Linq to LLBLGen Pro also offers a tracer, at two levels. For more information see Troubleshooting and Debugging.
Linq to LLBLGen Pro produces queries which can rely on a feature called
Derived tables, which means a separate query in the FROM
clause of the
SELECT
statement. Not all databases supported by LLBLGen Pro support
this feature: Firebird 1.x and SqlServer CE Desktop 3.1 or earlier don't
support derived tables. It's not recommended to use Linq to LLBLGen Pro
on these databases, use a version of the database which is newer
(Firebird 2.x / SqlServer CE Desktop 3.5).
SqlServer CE Desktop 3.5 is supported, however a limitation in SqlServer
CE Desktop 3.5 related to scalar queries in SELECT
statements (
SELECT (SELECT ...) As value, Foo FROM Bar..
) could lead to exceptions at
runtime when Linq is used together with SqlServer CE Desktop 3.5, so be
careful with scalar queries on SqlServer CE Desktop 3.x.
LinqMetaData
Linq queries can be split up in two groups: 1) the queries which work on an IEnumerable<T> and 2) the queries which work on an IQueryable<T>. Group 1) will make the compiler (C# or VB.NET) produce code which calls delegates and will execute the query entirely in memory.
The more popular name for this feature is Linq to Objects. The second group will make the compiler produce code which creates at runtime a tree of Expression instances, representing the entire query, in short an Expression tree. An Expression tree is not executable directly, it has to be interpreted to execute what is specified inside the Expression tree.
This is what a Linq provider, like Linq to LLBLGen Pro, does: it accepts an Expression tree, translates it into elements it can understand, interprets these elements and produces an executable form of this query. In Linq to LLBLGen Pro's case, it will produce a set of query API elements which can be passed to for example FetchEntityCollection on the specified DataAccessAdapter instance, or a GetMulti method call on the collection to fill.
Linq queries are self-contained. This means that they're both specification and also the result of the query: the variable which represents the query can be enumerated to read the results of that same query, and if the query produces a scalar, the variable representing the query also is the scalar value.
This requires that when writing the query in C# or VB.NET, the element which executes the query, i.e. Linq to LLBLGen Pro is also specified. This is done through LinqMetaData, a class present in the generated code's Linq folder (Adapter: DbGeneric project).
To tell the compiler that the query should be built into an Expression tree and be consumed by a Linq provider, the core object we refer to as the source of the data has to be an IQueryable<T> object or produce these objects. This is the LinqMetaData class. As a Linq query is self-contained, it also has to know how to execute itself, as it gets executed when it gets enumerated.
With SelfServicing this isn't a problem, SelfServicing code already knows how to read and write data, for Adapter this is a bit different: you need to pass the DataAccessAdapter instance to use to the LinqMetaData's constructor. You can also pass one later on, which is discussed below in the ILLBLGenProQuery section.
LinqMetaData is in the generated code, in the yourrootnamespace.Linq namespace, where yourrootnamespace is the root namespace you specified when you generated code. For Adapter users it's in the generated database generic project. This requires you to add the following statement to the top of your code file in which you want to write a Linq query to use Linq to LLBLGen Pro:
using *yourrootnamespace*.Linq;
Imports *yourrootnamespace*.Linq
Below shows the same query in both SelfServicing and Adapter. It defines a query to fetch all CustomerEntity instances from the USA.
using(var adapter = new DataAccessAdapter())
{
var metaData = new LinqMetaData(adapter);
var q = from c in metaData.Customer
where c.Country=="USA"
select c;
// enumerate over q here so it gets executed
}
var metaData = new LinqMetaData();
var q = from c in metaData.Customer
where c.Country=="USA"
select c;
As you can see, the queries for SelfServicing and adapter are identical. The only difference is the usage of LinqMetaData: with SelfServicing, it doesn't need a DataAccessAdapter instance. Keep in mind that the query 'q' in the examples above isn't executed till it is enumerated or you convert it into a List for example.
Scalar queries (like a query for the Count of all customers from a given country), are executed immediately however, as scalar queries can't be enumerated. This is a caveat of the Linq design by Microsoft. See the section about ILLBLGenProQuery below for details about how to execute a query in a different way than enumerating it.
SelfServicing: passing a Transaction instance
It could be that you want to execute a query inside a running transaction. With SelfServicing, it's necessary to add the elements to fetch to the running transaction to avoid deadlocks on for example SQL Server. With Linq, these elements aren't defined by you, all you do is define a query. To be able to execute this query inside a transaction, you can pass a Transaction instance to the LinqMetaData constructor:
var trans = new Transaction(IsolationLevel.ReadCommitted, "SSTest");
var metaData = new LinqMetaData(trans);
var q = from c in metaData.Customers select c;
Setting variables on the Linq provider.
As a Linq query is self-contained and can execute itself, it contains a Provider to make that happen. This Provider is accessible from the query (except when the query is a scalar query, as the query is executed immediately) and allows you to access the members of this provider, for example in the case of Adapter to set a DataAccessAdapter instance after the query has been constructed.
This can be handy if you have written some generic code which produces Linq queries on entities which can be fetched from different databases, depending on which DataAccessAdapter is used. The example below shows you how to access the provider and how to set its members.
// define the query
var q = from c in metaData.Customers select c;
// .. later set the adapter instance
using(var adapter = new DataAccessAdapter())
{
((LLBLGenProProvider2)((IQueryable)q).Provider).Adapter = adapter;
}
' define the query
Dim q = From c In metaData.Customers Select c
' .. later set the adapter instance
Using adapter As New DataAccessAdapter()
CType(CType(q, IQueryable).Provider, LLBLGenProProvider2).Adapter = adapter
End Using
ILLBLGenProQuery
An interface is defined on the IQueryable<T> elements produced by LinqMetaData: ILLBLGenProQuery. This interface allows you to execute the query by calling the Execute method. The advantage of this is that you can get the query result in its native container, e.g. an entity collection.
Another advantage is that to obtain a list of the results, the provider doesn't have to traverse the results in full, and copy over the results in a List: the returned results are already in the container they're initially stored in.
To use ILLBLGenProQuery.Execute and ILLBLGenProQuery.Execute<TResult>, the query created has to be an IQueryable query, so a query which results in one or more objects, not a scalar. Two extension methods to IQueryable are added to conveniently call ILLBLGenProQuery.Execute and ILLBLGenProQuery.Execute<TResult>.
The example below shows how to obtain the entity collection of the customers from the USA, using the IQueryable extension method.
var q = from c in metaData.Customer where c.Country=="Germany" select c;
var customers = q.Execute<EntityCollection<CustomerEntity>>();
var q = from c in metaData.Customer where c.Country=="Germany" select c;
CustomerCollection customers = q.Execute<CustomerCollection>();