LINQ - quanto foi inventado em C # apenas para que possamos desfrutar das delícias da Consulta Integrada à Linguagem. Nomeadamente:- Genéricos
- Métodos de extensão
- Expressões Lamda
- Árvores de expressão
- Tipos de Anonumus
- Inicializadores de objeto
- Tipo inferindo
E isso é tudo, para que possamos escrever algo como isto: var query =
from itemA in listA
join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
Não se pode discordar - impressiona.E no meio de todo esse açúcar sintático, havia uma colher de orvalho que me impedia de dormir o suficiente :)Essa é uma total falta de apoio para OUTER JOIN. Mas, como se viu, o alcatrão gira facilmente ... gira ... gira ...... em outro açúcar sintático.Aqueles que tentaram encontrar uma solução para o LEFT OUTER JOIN na Internet, provavelmente conhecem uma solução semelhante: var query =
from itemA in listA
join itemB in listB
on itemA.Key equals itemB.Key into outer
from itemO in outer.DefaultIfEmpty()
select new {itemA, itemO};
Esse projeto confunde claramente o entendimento por uma ordem de magnitude e complica uma construção já simples. E isso é apenas um substituto para INNER JOIN com LEFT OUTTER JOIN. Para não continuar chocando, não darei um exemplo com uma junção externa completa.Parece que seria simples se pudéssemos escrever assim: var query =
from itemA in listA
left join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
ou então var query =
from itemA in listA
full join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
Mas não. Os autores de C # não nos proporcionaram tanto prazer. Bem, isso não importa. No entanto, eles nos permitirão fazer isso sozinhos, embora não de uma maneira tão bonita.Para começar, se alguém lhe disser que o LINQ e a interface System.Collections.Generic.IEnumerable têm algo em comum e não podem existir separadamente, você pode rir com segurança pessoalmente ...Design var query =
from itemA in listA
join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
é simplesmente traduzido pelo compilador na seguinte sequência de caracteres: var query = listA.Join(listB, itemA => itemA.Key, itemB => itemB.Key, (itemA, itemB) => new {itemA, itemB});
e não importa que tipo de variáveis listA e listB sejam. Suponha que a lista A seja uma variável do tipo Tipo A e o item permanente B seja do tipo Tipo B. Portanto, se TypeA e TypeB contiverem uma propriedade ou campo chamado Key, TypeA conterá o método Join () com 4 argumentos. Essa consulta LINQ é compilada livremente.Ao usar variáveis no LINQ que implementam a interface IEnumerable padrão, o método de extensão é usadopublic class System.Linq.Enumerable
{
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector) {...}
}
Na verdade, esse método também produz o conhecido INNER JOIN. E agora a magia das ruas começa. Para implementar a JUNTA EXTERIOR ESQUERDA / DIREITA / CHEIA (ou JUNTA que agradará sua alma), é necessário substituir a chamada do método padrão pelo implementado por nós. Para fazer isso, precisamos converter a variável listA de alguma forma em um tipo que possamos controlar.Implementando as duas classes a seguir:public class JoinedEnumerable<T> : IEnumerable<T>
{
public readonly IEnumerable<T> Source;
public bool IsOuter;
public JoinedEnumerable(IEnumerable<T> source) { Source = source; }
IEnumerator<T> IEnumerable<T>.GetEnumerator() { return Source.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return Source.GetEnumerator(); }
}
public static class JoinedEnumerable
{
public static JoinedEnumerable<TElement> Inner<TElement>(this IEnumerable<TElement> source)
{
return Wrap(source, false);
}
public static JoinedEnumerable<TElement> Outer<TElement>(this IEnumerable<TElement> source)
{
return Wrap(source, true);
}
public static JoinedEnumerable<TElement> Wrap(IEnumerable<TElement> source, bool isOuter)
{
JoinedEnumerable<TElement> joinedSource
= source as JoinedEnumerable<TElement> ??
new JoinedEnumerable<TElement>(source);
joinedSource.IsOuter = isOuter;
return joinedSource;
}
}
podemos escrever facilmente a próxima consulta LINQ var query =
from itemA in listA.Outer()
join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
e agora implementando o método de extensão Join para a classe JoinEnumerable da maneira que precisamos, obtemos tudo o que precisamos.E aqui estão os métodos de extensão:public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this JoinedEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer = null)
{
if (outer == null) throw new ArgumentNullException("outer");
if (inner == null) throw new ArgumentNullException("inner");
if (outerKeySelector == null) throw new ArgumentNullException("outerKeySelector");
if (innerKeySelector == null) throw new ArgumentNullException("innerKeySelector");
if (resultSelector == null) throw new ArgumentNullException("resultSelector");
bool leftOuter = outer.IsOuter;
bool rightOuter = (inner is JoinedEnumerable<TInner>) && ((JoinedEnumerable<TInner>)inner).IsOuter;
if (leftOuter && rightOuter)
return FullOuterJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
if (leftOuter)
return LeftOuterJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
if (rightOuter)
return RightOuterJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
return Enumerable.Join(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
}
public static IEnumerable<TResult> LeftOuterJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer = null)
{
var innerLookup = inner.ToLookup(innerKeySelector, comparer);
foreach (var outerItem in outer)
foreach (var innerItem in innerLookup[outerKeySelector(outerItem)].DefaultIfEmpty())
yield return resultSelector(outerItem, innerItem);
}
public static IEnumerable<TResult> RightOuterJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer = null)
{
var outerLookup = outer.ToLookup(outerKeySelector, comparer);
foreach (var innerItem in inner)
foreach (var outerItem in outerLookup[innerKeySelector(innerItem)].DefaultIfEmpty())
yield return resultSelector(outerItem, innerItem);
}
public static IEnumerable<TResult> FullOuterJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer = null)
{
var outerLookup = outer.ToLookup(outerKeySelector, comparer);
var innerLookup = inner.ToLookup(innerKeySelector, comparer);
foreach (var innerGrouping in innerLookup)
if (!outerLookup.Contains(innerGrouping.Key))
foreach (TInner innerItem in innerGrouping)
yield return resultSelector(default(TOuter), innerItem);
foreach (var outerGrouping in outerLookup)
foreach (var innerItem in innerLookup[outerGrouping.Key].DefaultIfEmpty())
foreach (var outerItem in outerGrouping)
yield return resultSelector(outerItem, innerItem);
}
Voila ...Linda ESQUERDA JUNTA EXTERNA: var query =
from itemA in listA.Outer()
join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
JOGO EXTERIOR DIREITO Bonito: var query =
from itemA in listA.Inner()
join itemB in listB.Outer()
on itemA.Key equals itemB.Key
select new {itemA, itemB};
JOGO EXTERIOR COMPLETO: var query =
from itemA in listA.Outer()
join itemB in listB.Outer()
on itemA.Key equals itemB.Key
select new {itemA, itemB};
Agora, se desejar, você pode usar sua própria abordagem - já que o campo da imaginação é enorme aqui. Eu tenho várias soluções mais interessantes para implementar brindes no zashashnik. Haverá tempo para compartilhá-los.Obrigado pela atenção.Que a POWER esteja com você!