Joining Tables Using LINQ
You may have noticed in the preceding code that the Join
extension method is included in the list of inefficient extension
methods and as such is not permitted within LINQ
to SharePoint. However, this does not mean that retrieving related data
isn’t possible, only that arbitrary joins are not supported. Where the
relationship between two entities is defined by a lookup column,
retrieving related data is permitted and in fact is actually achieved
using a much simpler syntax than is required by the Join operator.
Simple Join
Let’s add a new button to enable us to execute a basic join query. Label the button Basic Join Query and in the event handler add the following code:
private void button8_Click(object sender, EventArgs e)
{
using(HireSampleDataContext dxRead = new HireSampleDataContext(SiteUrl.Text))
{
StringBuilder sb = new StringBuilder();
using (StringWriter logWriter = new StringWriter(sb))
{
//log the generated CAML query to a StringWriter
dxRead.Log = logWriter;
dxRead.ObjectTrackingEnabled = false;
var basicQuery = from a in dxRead.OnHireAssets
where a.ContractReference.ContractStartDate.Value
< DateTime.Now
orderby a.AssetTag,
a.ContractReference.ContractStartDate
select new
{
a.AssetTag,
a.AssetId,
a.ContractReference.ContractId,
a.ContractReference.ContractStartDate,
a.ContractReference.ContractEndDate
};
dataGridView1.DataSource = basicQuery.ToList();
}
//create a temporary file for the generated CAML
string fileName = Path.Combine(Path.GetTempPath(), "tmpCaml.xml");
XmlDocument doc = new XmlDocument();
doc.LoadXml(sb.ToString());
doc.Save(fileName);
//point the browser control to the temporary generated CAML file
webBrowser1.Navigate(fileName);
}
}
From this code example, you can see that the join is
performed by making use of the lookup field. Within the LINQ query, the
lookup field is of the same type as the item to which it refers. In
this case, the ContractReference lookup field is implemented as a property of type HireContract.
This allows you to deal with entity objects within a logical hierarchy,
ignoring the underlying data structure implementation. This is much
simpler than the Join extension
method syntax, which relies on your knowledge of the data structure to
know which tables to join together and which fields to use for the join.
Complex Join
Using this syntax, you can join multiple lists
together as long as appropriate lookup fields have been defined. To
explore this, we’ll add another button, this time labeled Complex Join Query with the following code:
private void button9_Click(object sender, EventArgs e)
{
using(HireSampleDataContext dxRead = new HireSampleDataContext(SiteUrl.Text))
{
StringBuilder sb = new StringBuilder();
using (StringWriter logWriter = new StringWriter(sb))
{
//log the generated CAML query to a StringWriter
dxRead.Log = logWriter;
dxRead.ObjectTrackingEnabled = false;
var basicQuery = from n in dxRead.AssetNotes
where int.Parse(n.LocationCode.Substring(8))==2
&& n.AssetReference.ContractReference.ContractStartDate.Value
< DateTime.Now
orderby n.LocationCode,
n.AssetReference.AssetId,
n.AssetReference.ContractReference.ContractStartDate
select new
{
n.LocationCode,
n.AssetReference.AssetTag,
n.AssetReference.AssetId,
n.AssetReference.ContractReference.ContractId,
n.AssetReference.ContractReference.ContractStartDate,
n.AssetReference.ContractReference.ContractEndDate
};
dataGridView1.DataSource = basicQuery.ToList();
}
//create a temporary file for the generated CAML
string fileName = Path.Combine(Path.GetTempPath(), "tmpCaml.xml");
XmlDocument doc = new XmlDocument();
doc.LoadXml(sb.ToString());
doc.Save(fileName);
//point the browser control to the temporary generated CAML file
webBrowser1.Navigate(fileName);
}
}
In
this sample, we’ve joined all three lists together and have also
performed some string processing as part of the Where clause. You’ll
notice from the generated CAML that the Location Code filter is not
included in the query; that’s because the LINQ provider brings back the
resulting rows and then programmatically applies the additional filter
in memory before returning the results. Again, this can be done
efficiently because only one CAML query is required, so the resource
usage is minimal.