Linq to LLBLGen Pro: feature highlights, part 2
In the first part of this series
I talked about the fact that Linq to LLBLGen Pro is a
full implementation of Linq and why it's so important
to use a full linq provider instead of a half-baked one.
Today, I'll discuss a couple of native
LLBLGen Pro
features we've added to our Linq provider via extension
methods: hierarchical fetches and exclusion of entity fields
in a query. Furthermore some other features will be in the
spotlight as well. What I also want to highlight is that
using an O/R mapper is more than just filling dumb classes
with dumb data: it's entity management, and the O/R
mapper framework should offer you tools so you will
be able to manage and do whatever you want with the entity
graph in memory with as less problems and friction as
possible. After all, the task you have isn't writing
infrastructure code, entity classes nor code to make these
interact with eachother, your task is to write code which
consumes these classes, and works with these
classes. This thus means that you should be able to work on
that code from the get-go, as that's what your client
expects from you
.
Exclusion / inclusion of entity fields in a query
The first feature I want to highlight today is the exclusion
of entity fields in a query. Say you want to fetch a set of
entities and the entities contain one or more large fields,
e.g. a blob/image field or a text/clob field. If you don't
need these large fields, it's useless to fetch them in your
query, as the transportation of the large data (which can be
many megabytes) could make the query a slow performer, as
all the data has to be fetched by the database and send over
the wire. LLBLGen Pro has a feature called
Exclusion / Inclusion of fields, which allows you to
exclude a set of fields from an entity when fetching one or
more instances of that entity (exclusion). You can also
specify the fields you want (inclusion) if you want to fetch
just a few fields from an entity which has a lot of fields
for example. If you want to fetch the fields back into the
entities, that's possible too, LLBLGen Pro offers a special
mechanism for that which efficiently fetches the excluded
field data into the existing entities. We'll see an example
of that later on in this post.
For this example, we'll fetch a set of Northwind Employee
instances. The Northwind Employee entity has two large
fields: an image (Photo) and an ntext field (Notes).
Initially we'll fetch all the Employee entities using Linq
and exclude the two fields, Photo and Notes:
// Listing 1
EntityCollection<EmployeeEntity> employees = null;
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
LinqMetaData metaData = new LinqMetaData(adapter);
var q = (from e in metaData.Employee
select e).ExcludeFields(e => e.Photo, e => e.Notes);
// consume 'q' here. Use the Execute method to return an entity collection.
employees = ((ILLBLGenProQuery)q).Execute<EntityCollection<EmployeeEntity>>();
}
The code uses Lambda expressions which offer compile time
checked correctness. Later on, we'll see how fields also can
be excluded in hierarchical fetches. One could argue that
this also can be achieved by a projection onto the
EmployeeEntity type using a select new {} statement.
That's true in theory, but it will likely be more work (as
you have to specify all fields you do want) and it also will
use a different pipeline internally (namely the one for
custom types being fetched through a projection), and not
the entity fetch pipeline.
This might sound strange but fetching entities is more than
just putting data into a class instance. The biggest hurdle
is inheritance. If you do a new projection, the instances to
create are known: they're instances of the type specified in
the projection, be it an anonymous type or a specific type.
With entity fetches this is different: the type to
instantiate is determined based on the data received from
the database. What if the type specified in the projection
isn't a known entity type? How can the system then create an
instance of a subtype of that type if the data received from
the database is the data of a subtype? Only in the case
where the developer has specified a class of a known entity
type, the same pipeline can be used, but that's not always
the case, as the developer is allowed to specify any type,
including anonymous types, as shown in the following
example:
q = q.TakePage(2, 3);
the framework will fetch page 2 of size 3 with Customer
instances from the total set of Customer instances from
Germany. The 3 Customer instances will be fetched together
with their related entities as defined in the Prefetch Path.
As everyting is inside a graph, I can navigate that graph
using normal property navigation. Also, because all
collections of entities inside entities (e.g.
customer.Orders) are entity collections, I can create
entity views on them, similar to what I've showed
above, and filter them, sort them and project them in-memory
without touching the original collection. Don't make the
mistake that this is similar to just running a Linq to
Objects query on the collection: if I bind an entity view to
a grid and add a row (which is a new entity), it's added to
the collection. If I remove an entity from the collection
and it happens to be in, say 3 entity views, it's removed
from those 3 views as well. An entity view is a
live view on a subset of the entity collection, with
the awareness as if you're handling the collection.
Prefetch Paths of course support inheritance and are fully
polymorphic. This means that you can specify path branches
which are solely for some subtypes of a given entity
fetched. This way, you're able to specify very powerful
paths to fetch complex graphs with very little code.
There is another form of hierarchical fetches, using
nested queries inside the projection, as I've
described last time in short and also more in detail in
part 14
of the
Developing Linq to LLBLGen Pro articles, so rehashing here what's said there is a bit redundant.
I'd recommend you to read
part 14
if you're interested in how this works behind the scenes and
why our mechanism is more efficient than say the one inside
Linq to Sql
.
Next time I'll discuss more in depth the advanced method
mapping capabilities in Linq to LLBLGen Pro to map .NET
constructs onto database constructs, and will also give an
example of how LLBLGen Pro's authorization feature works
nicely with the Linq queries, thanks to our Dependency
Injection framework, so you can exclude entities, hide data
etc. based on the user using the data through authorizers
you write yourself. Stay tuned!