LINQ to SQL – Left Outer Join com várias condições de junit

Eu tenho o seguinte SQL, que estou tentando traduzir para LINQ:

SELECT f.value FROM period as p LEFT OUTER JOIN facts AS f ON p.id = f.periodid AND f.otherid = 17 WHERE p.companyid = 100 

Eu tenho visto a implementação típica da junit externa esquerda (ou seja, into x from y in x.DefaultIfEmpty() etc.), mas não tenho certeza de como introduzir a outra condição de junit ( AND f.otherid = 17 )

EDITAR

Por que o AND f.otherid = 17 condição parte do JOIN em vez de na cláusula WHERE? Porque f pode não existir para algumas linhas e eu ainda quero que essas linhas sejam incluídas. Se a condição for aplicada na cláusula WHERE, depois do JOIN, não obtenho o comportamento desejado.

Infelizmente isso:

 from p in context.Periods join f in context.Facts on p.id equals f.periodid into fg from fgi in fg.DefaultIfEmpty() where p.companyid == 100 && fgi.otherid == 17 select f.value 

parece ser equivalente a isso:

 SELECT f.value FROM period as p LEFT OUTER JOIN facts AS f ON p.id = f.periodid WHERE p.companyid = 100 AND f.otherid = 17 

que não é bem o que eu sou depois.

Você precisa apresentar sua condição de associação antes de chamar DefaultIfEmpty() . Gostaria apenas de usar a syntax do método de extensão:

 from p in context.Periods join f in context.Facts on p.id equals f.periodid into fg from fgi in fg.Where(f => f.otherid == 17).DefaultIfEmpty() where p.companyid == 100 select f.value 

Ou você poderia usar uma subconsulta:

 from p in context.Periods join f in context.Facts on p.id equals f.periodid into fg from fgi in (from f in fg where f.otherid == 17 select f).DefaultIfEmpty() where p.companyid == 100 select f.value 

isso também funciona … se você tiver várias colunas

 from p in context.Periods join f in context.Facts on new { id = p.periodid, p.otherid } equals new { f.id, f.otherid } into fg from fgi in fg.DefaultIfEmpty() where p.companyid == 100 select f.value 

Eu sei que é ” um pouco tarde “, mas apenas no caso, se alguém precisa fazer isso na syntax LINQ Method ( é por isso que eu encontrei este post inicialmente ), isso seria como fazer isso:

 var results = context.Periods .GroupJoin( context.Facts, period => period.id, fk => fk.periodid, (period, fact) => fact.Where(f => f.otherid == 17) .Select(fact.Value) .DefaultIfEmpty() ) .Where(period.companyid==100) .SelectMany(fact=>fact).ToList(); 

Outra opção válida é espalhar as junções entre várias cláusulas LINQ , da seguinte maneira:

 public static IEnumerable GetSiteContent(string pageName, DateTime date) { IEnumerable content = null; IEnumerable addMoreContent = null; try { content = from c in DB.Announcementboards //Can be displayed beginning on this date where c.Displayondate > date.AddDays(-1) //Doesn't Expire or Expires at future date && (c.Displaythrudate == null || c.Displaythrudate > date) //Content is NOT draft, and IS published && c.Isdraft == "N" && c.Publishedon != null orderby c.Sortorder ascending, c.Heading ascending select c; //Get the content specific to page names if (!string.IsNullOrEmpty(pageName)) { addMoreContent = from c in content join p in DB.Announceonpages on c.Announcementid equals p.Announcementid join s in DB.Apppagenames on p.Apppagenameid equals s.Apppagenameid where s.Apppageref.ToLower() == pageName.ToLower() select c; } //CROSS-JOIN this content content = content.Union(addMoreContent); //Exclude dupes - effectively OUTER JOIN content = content.Distinct(); return content; } catch (MyLovelyException ex) { throw ex; } } 

Parece-me que há valor em considerar algumas reescritas no seu código SQL antes de tentar traduzi-lo.

Pessoalmente, eu escreveria uma consulta como um sindicato (embora eu evitasse nulos inteiramente!):

 SELECT f.value FROM period as p JOIN facts AS f ON p.id = f.periodid WHERE p.companyid = 100 AND f.otherid = 17 UNION SELECT NULL AS value FROM period as p WHERE p.companyid = 100 AND NOT EXISTS ( SELECT * FROM facts AS f WHERE p.id = f.periodid AND f.otherid = 17 ); 

Então eu acho que eu concordo com o espírito da resposta do @ MAbraham1 (embora o código deles não esteja relacionado à questão).

No entanto, parece que a consulta foi expressamente projetada para produzir um único resultado de coluna, incluindo linhas duplicadas – na verdade, valores nulos duplicados! É difícil não chegar à conclusão de que essa abordagem é falha.