Chapter 7. Extending LINQ
In closing, let's touch on the extensibility of LINQ, another of its elegant design characteristics. As you have seen, LINQ interoperates with a range of data sources, from object graphs to relational databases to XML documents. Let's look at a quick example of how we go about extending one of our own classes to natively support LINQ.
Our running demo has focused on
doctors
working at the fictitious University Hospital. You've seen many queries involving
doctors
, often using the
Distinct
operator to eliminate duplicates. For example, a query to yield the distinct doctors working in October 2006:
DataSets.SchedulingDocs ds = FillDataSet();
var oct2006 = (
from doc in ds.Doctors
join call in ds.Calls
on doc.Initials equals call.Initials
where call.DateOfCall >= new DateTime(2006, 10, 1) &&
call.DateOfCall <= new DateTime(2006, 10, 31)
select doc
)
.Distinct();
Since we designed the data model, we know that a doctor's initials are unique. We can take advantage of this information to optimize the application of
Distinct
in the query above by providing our own implementation specific to our DataSet:
using DS = DataSets.SchedulingDocs;
public static class
DataSetsExtension
{
public static IEnumerable<DS.DoctorsRow>
Distinct
(this
IEnumerable<DS.DoctorsRow> src)
{
// optimize by using a hashtable with doctors' initials:
System.Collections.Hashtable ht = new System.Collections.Hashtable();
foreach(var doc in src) // for each doctor in the original sequence:
{
if (ht[doc.Initials] == null) // first time, so remember and return:
{
ht.Add(doc.Initials, doc.Initials);
yield return doc;
}
else // we've yielded this doc already, so skip:
continue;
}
}
}//class
For sequences of type
DoctorsRow
i.e., doctors in our DataSetthis implementation will be used in place of the standard LINQ implementation of
Distinct
. Thus, for any query of the form,
var query = from doc in ds.Doctors
.
.
select doc;
the application of
Distinct
will invoke our optimized implementation. That's it!
|