Joins.

Specifying joins is a key element of the QuerySpec API. It allows to specify joins with both EntityRelation objects, which are created using the generated code, as well as using extension methods. One can mix them to build joins.

Joins have to be specified inside a call to .From(). For DynamicQuery instances, this is straight forward. For EntityQuery<T> instances, this needs a construct called QueryTarget:

var q = qf.Customer
            .From(QueryTarget.InnerJoin(qf.Order)
                    .On(CustomerFields.CustomerId==OrderFields.OrderId))

For EntityQuery<T> instances, it's essential to start the series of joins passed as argument to From() with QueryTarget as the first left operand. DynamicQueries don't have a natural source, so in these cases, one always has to specify the full left operand.

Using QueryTarget in From() calls on a DynamicQuery / DynamicQuery<T> is allowed but only when additionally join fragments are appended to an existing query (so effectively, you're calling From() multiple times on the DynamicQuery / DynamicQuery<T>).

Info

Calling .From() multiple times will overwrite the From clause of the previous call unless you start the argument passed to From() with QueryTarget.

Method based join building

The methods .InnerJoin(), .LeftJoin(), .RightJoin(), .FullJoin() and .CrossJoin() can be called inside a From() call on an EntityQuery<T> to join that entity query with a related element, which can be another query.

There are also overloads which accept an EntityRelation object. This can have the advantage where the user doesn't want to fill in the On clause, as that's already specified by the EntityRelation object. When an EntityRelation object is specified, the start entity has to match the T type of the EntityQuery<T>.

The method called decides which JoinHint is used when adding the EntityRelation to the overall RelationCollection.

Examples:

var qf = new QueryFactory(); 
// using a related element query and an On() clause. 
var q = qf.Employee 
            .From(QueryTarget 
                .LeftJoin(qf.Order)
                        .On(EmployeeFields.EmployeeId==OrderFields.EmployeeId));

// using an EntityRelation object
var q2 = qf.Employee
            .From(QueryTarget
                    .InnerJoin(EmployeeEntity.Relations.OrderEntityUsingEmployeeId));

With a DynamicQuery, QueryTarget can only be used in subsequential calls. Calling the join methods on a dynamic query like:

// Incorrect!
var qf = new QueryFactory(); 
var q = qf.Create() 
    .From(QueryTarget.InnerJoin(qf.Order).On(some predicate)); 

This is not correct, although the api allows it, because a dynamic query doesn't have a default source. Instead, use the From() method for dynamic queries:

// correct 
var qf = new QueryFactory(); 
var q = qf.Create() 
            .From(qf.Customer 
                .InnerJoin(qf.Order)
                    .On(CustomerFields.CustomerId==OrderFields.CustomerId));

To append joins to an existing dynamic query, QueryTarget is required, otherwise the second From() call will overwrite the first one:

// correct, second call will append join to first From()'s argument.
var q = qf.Create()
            .From(qf.Customer.InnerJoin(qf.Order)
                      .On(CustomerFields.CustomerId.Equal(OrderFields.CustomerId)));
//....
// append another join correctly: 
q.From(QueryTarget.InnerJoin(qf.OrderDetails)
            .On(OrderFields.OrderID.Equal(OrderDetailFields.OrderId)));


// The following is Incorrect: second call will replace the from clause of the first. 
var q = qf.Create()
            .From(qf.Customer.InnerJoin(qf.Order)
                      .On(CustomerFields.CustomerId.Equal(OrderFields.CustomerId)));
// ....
// will replace from clause already there.
q.From(qf.Order.InnerJoin(qf.OrderDetails)
            .On(OrderFields.OrderID.Equal(OrderDetailFields.OrderId))); 

For dynamic queries, to start a join with an EntityRelation object and pass it to the From() method, use Joins.joinmethod. For example, to create an Inner join using the join above but with entityrelation objects, one should use:

// correct 
var qf = new QueryFactory(); 
var q = qf.Create() 
            .From(Joins.Inner(CustomerEntity.Relations.OrderEntityUsingCustomerId));

As the .InnerJoin etc. methods work on an IJoinOperand object, this code compiles:

// Incorrect!
var qf = new QueryFactory(); 
// using a related element query and an On() clause. 
var q = qf.Employee.LeftJoin(qf.Order).On(EmployeeFields.EmployeeId==OrderFields.EmployeeId));

However, this isn't correct as 'q' now is an InnerOuterJoin object, not a query.