2.4 One-to-One Join Using a Subquery
A subquery in a queries Select projection can
achieve a one-to-one join. In testing, this option did not perform as
well as the alternatives and should only be considered for very small
(less than ten-element) sequence sizes. This technique simply looks up
related data from a second sequence within the first query’s select
projection. Listing 4 demonstrates the subquery technique, and the result is shown in Output 4.
Listing 4. One-to-One Join using Subquery syntax—see Output 4
var customers = Customer.SampleData(); var orders = Order.SampleData();
var q2 = from o in orders select new { OrderNumber = o.OrderNumber, LastName = (from c in customers where c.CustomerID == o.CustomerID select c.LastName).SingleOrDefault() };
foreach (var order in q2) Console.WriteLine( "Customer: {0} Order Number: {1}", order.LastName.PadRight(9, ' '), order.OrderNumber);
|
Output 4.
Customer: Gottshall Order Number: Order 1 Customer: Gottshall Order Number: Order 2 Customer: Deane Order Number: Order 3 Customer: Deane Order Number: Order 4 Customer: Zeeman Order Number: Order 5
|
2.5 One-to-One Join Using SingleOrDefault Operator
The SingleOrDefault
standard query operator returns the single matching element from
another sequence or null if a matching value cannot be found (if more
than one matching element is found, an exception is thrown). This is
ideal for looking up a matching record in another sequence and making
that data accessible to your query—in other words, a one-to-one join.
This technique is fast and clean when looking up only a single or a few
related records.
Listing 5 demonstrates a one-to-one join using the SingleOrDefault technique, which generates the output shown in Output 5.
Listing 5. One-to-One Join using the SingleOrDefault operator—see Output 5
var customers = Customer.SampleData(); var orders = Order.SampleData();
var q3 = from o in orders let cust = customers .SingleOrDefault( c => c.CustomerID == o.CustomerID) select new { OrderNumber = o.OrderNumber, LastName = cust.LastName };
foreach (var order in q3) Console.WriteLine( "Customer: {0} Order Number: {1}", order.LastName.PadRight(9, ' '), order.OrderNumber);
|
2.6 One-to-One Join Using Cross Join
This
join type matches traditional relational database join techniques.
However, because LINQ to Objects doesn’t optimize the query (by
maintaining indexes, building statistics, rewriting queries, and so on)
like most good database server products, one-to-one joins using cross
joins should be avoided because performance is poor and the
alternatives previously shown are just as easy.
This technique cross joins two sequences to get the Cartesian Product (every element against every element) and then uses a where
clause to keep only the matching outer and inner records. This achieves
the goal of a one-to-one join, but at great expense in performance due
to the number of elements enumerated and never passing the filter
criteria. Use this technique sparingly and only if collection sizes are
very small.
Listing 6 demonstrates a one-to-one join using the cross join/where technique and generates the code shown in Output 6.
Listing 6. One-to-one join using a cross join and where filter—see Output 6
var customers = Customer.SampleData(); var orders = Order.SampleData();
var q4 = from o in orders from c in customers where c.CustomerID == o.CustomerID select new { o.OrderNumber, c.LastName };
foreach (var order in q4) Console.WriteLine( "Customer: {0} Order Number: {1}", order.LastName.PadRight(9, ' '), order.OrderNumber);
|
Output 6.
Customer: Gottshall Order Number: Order 1 Customer: Gottshall Order Number: Order 2 Customer: Deane Order Number: Order 3 Customer: Deane Order Number: Order 4 Customer: Zeeman Order Number: Order 5
|