Es algo así como una unión. Cuando uno dice:
from customer in customers
join order in orders on customer.Id equals order.CustomerId
select whatever
que es esencialmente una forma más eficiente de la escritura:
from customer in customers
from order in orders
where customer.Id == order.CustomerId
select whatever
¿Ves por qué? El primero dice "hey query processor, los clientes y los pedidos tienen una relación especial que se define por la igualdad de la identificación del cliente y la identificación del cliente almacenada en un pedido". El segundo dice "dame el producto cartesiano, todas las combinaciones posibles de clientes y pedidos, y luego filtra los que no tienen sentido". Tienen el mismo efecto, pero el primero es más eficiente.
Sin embargo, puede utilizar varias cláusulas "de" para hacer cosas que son más sofisticadas que solo los productos cartesianos. Supongamos que un cliente puede tener más de una dirección:
from customer in customers
from address in customer.Addresses
select address
múltiple from
cláusulas son en realidad un "seleccione muchos". Es decir, toman una secuencia, y una forma de hacer secuencias de cada elemento de la primera secuencia, y fusionan todas las secuencias resultantes juntas.
El "seleccionar muchos" es simple pero extremadamente poderoso; Ya hemos visto que puede usar "seleccionar muchos" para realizar una operación de unión (lenta, pero correcta). De hecho, puede usar seleccionar muchos para hacer todas las consultas posibles si es lo suficientemente inteligente y no le importa perder mucho tiempo y memoria. Por ejemplo:
from customer in customers
where customer.City == "London"
select customer
se podría escribir sin "donde" de esta manera:
from customer in customers
from c in (customer.City == "London" ?
new Customer[] {customer} :
new Customer[] { })
select c;
que sería una locura hacerlo, pero where
y join
esté innecesaria - que son sólo formas más rápidas, más cortas y más eficientes de escribir un seleccionar muchos.