Tuesday, December 16, 2008

LINQ of the Day

Today's LINQ covers a several points: Providing an object data source, suitable for binding. Making a query reusable, and pre-compiling it for best performance. Selecting useful objects.

Here's the code:

public static List<IndependentRep> IndependentRepList()
using (FSP dc = FSP.DC())
return GetIndependentRepQuery(dc).ToList();

private static Func<FSP, IQueryable<IndependentRep>>
GetIndependentRepQuery = CompiledQuery.Compile(
(FSP dc) => from u in dc.Pm_Logins
where (!u.RecordDeleted) &&
(u.LoginType == Util.c_Login_IndependentSalesRep)
select new IndependentRep
UserName = u.UserName,
RepContact = new Contact
FirstName = u.FirstName,
LastName = u.LastName,
OfficePhone = u.OfficePhone,
MobilePhone = u.MobilePhone,
Email = u.Email,
CompanyName = u.CompanyName
Status = u.Status

Providing an Object Data Source

You'll notice that there are two static functions here, a public static function that calls a private static function. The public function has no arguments, so it's ideal for binding with an object data source. It could pull parameters for session data or the application cache, if the scope of the data needed to be constrained based on context, but we're not doing that here. All we do is grab a data context and call the private function, then return the results as a list.

Reusable Pre-Compiled Queries

This might look a bit intimidating at first, but it's well worth the effort to learn and use. The CompiledQuery.Compile method provides a way to compile a LINQ query and cache the expression tree, so that it can be re-used without the overhead of re-evaluating the query each time. The query is compiled on first use, and (as far as I know) has the lifetime of the AppDomain.

The Compile method is overloaded to allow you to pass zero to three arguments to the query. We're not passing any arguments to the query itself in this case, but if we were, they would come between the first and last arguments listed in "Func". "Func" is a generic delegate template, so at this point we're specifying types. The first type is always the data context, and the last argument is always the return type of the query. By leaving the return type as IQueryable<T>, callers can always add more LINQ methods to further refine the query before it goes to the database. (And remember, we're doing the ultimate in deferred query execution here; the query never gets executed inside this function.)

CompiledQuery.Compile actually takes only one argument, and that's a lambda expression that defines the query itself. The arguments to the lambda expression are all of the types listed in "Func" except the return type. In defining the arguments for the lambda expression, we're providing both types and parameter names, and the parameter names are used inside the query. To the right of the lambda operator ("=>"), we have a normal LINQ query, with the exception that you must return a named class type that matches the return type in "Func". That class can be either an entity known to your data context, or it can be a class defined in your application.

Selecting Useful Objects

Look at the select clause in the query. Select always returns one object, in this case a new instance of type IndependentRep. IndependentRep is an application-defined class with three members:

public class IndependentRep
public string UserName
{ get; set; }
public Contact RepContact
{ get; set; }
public string Status
{ get; set; }

As we initialize the object in the select clause, we can assign values to UserName and Status directly. We can also create a new instance of a Contact class, and populate that as well with data from the query. In this case, we're generating a simple class for use in databinding, but you could also have the select clause generate a pre-populated business entity, a class with its own business rules and perhaps its own persistence and update methods.

Putting LINQ into production means being comfortable with selecting from the many features of LINQ, and combining these into coherent and useful units of work. This is a real-life example.

No comments: