2011-12-16 31 views
6

Ho due tabelle di database Contatto (Id, Nome, ...) e ContactOperationalPlaces (ContactId, MunicipalityId), dove un contatto può essere collegato a più ContactOperationalPlaces.Come aggiungere una clausola where su un join linq (lambda)?

Quello che sto cercando di fare è creare una query (ASP .NET, C#) con IQueryable, che seleziona solo tutti i contatti esistenti nella tabella ContactOperationalPlaces, con un determinato ID di dominio.

la query SQL simile alla seguente:

select * from Contacts c 
right join ContactOperationPlaces cop on c.Id = cop.ContactId 
where cop.MunicipalityId = 301; 

Con LINQ sarebbe simile a questa:

//_ctx is the context 
var tmp = (from c in _ctx.Contacts 
      join cop in _ctx.ContactOperationPlaces on c.Id equals cop.ContactId 
      where cop.MunicipalityId == 301 
      select c); 

Quindi, so come fare questo se il punto è stato quello di selezionare tutti questo in una volta, sfortunatamente non lo è. Sto costruendo una query basata sull'input dell'utente, quindi non conosco tutta la selezione in una volta.

Quindi questo è ciò che il mio codice è simile:

IQueryable<Contacts> query = (from c in _ctx.Contacts select c); 
//Some other logic.... 
/*Gets a partial name (string nameStr), and filters the contacts 
so that only those with a match on names are selected*/ 
query = query.Where(c => c.Name.Contains(nameStr); 
//Some more logic 
//Gets the municipalityId and wants to filter on it! :(how to? 
query = query.where(c => c.ContactOperationalPlaces ...........?); 

La differenza con i due in cui dichiarazioni è che con il primo, ogni contatto ha un solo nome, ma con quest'ultimo un contatto può contenere diversi posti operativi ...

Sono riuscito a trovare una soluzione, ma questa soluzione mi dà un oggetto non identificato, che contiene entrambi i tavoli. E non so come procedere.

query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId, 
     (c, cop) => new {c, cop}).Where(o => o.cop.municipalityId == 301); 

L'oggetto restituito da questa espressione è System.Linq.Iqueryable < {c: Contatto, cop: ContactOperationalPlace}>, e non può essere lanciato a Contatti ...

Così, che è il problema. La risposta è probabilmente piuttosto semplice, ma proprio non riesco a trovarla ...

risposta

11

Si crea un tipo anonimo con entrambi gli oggetti prima della clausola WHERE e filtro sul valore ContactOperationPlaces. Devi solo selezionare il contatto dopo.

query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId, 
      (c, cop) => new {c, cop}).Where(o => o.cop.municipalityId == 301) 
            .Select(o => o.c) 
            .Distinct(); 
+2

e aggiungere distinte() per il contatto probabilmente –

+0

Grazie per il correzione. –

+0

Grazie mille! Sapevo che la soluzione sarebbe stata piuttosto semplice una volta trovata. – linnkb

0

puoi lanciarlo su var e provare ad usare intellisense su di esso?

var myCast = query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId, 
    (c, cop) => new {c, cop}).Where(o => o.cop.municipalityId == 301); 

Solo un pensiero

1

Non è necessario restituire nuovi oggetti nella funzione di selezione dei risultati. Il delegato fornisce entrambe le variabili in modo da poter scegliere l'una o l'altra o qualche altra variazione (che richiederebbe un nuovo oggetto). Prova questo:

query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId, 
    (c, cop) => c).Where(o => o.cop.municipalityId == 301); 
+0

Ho già provato questo, e dal momento che non faccio così: (c, poliziotto) => nuovo {c, poliziotto}, non avrò la possibilità di selezionare poliziotto nella clausola where ... – linnkb

0

penso che sarebbe molto più facile se si avvia questo come 2 query diverse, quindi combinarli. Suppongo che la relazione sia Contact (1 < -> molti) Contactoperationplaces? E alla fine, mostrerai 1 oggetto per Contactoperationplaces, non 1 item per Contact?

fare in questo modo:

IQueryable<Contacts> query = (from c in _ctx.Contacts select c); 

... 

query = query.Where(x=> x.Name.ToLower().Contains(nameStr.ToLower()); 

... 

IQueryable<ContactOperationPlaces> query_2 = 
    (from c in _ctx.ContactOperationPlaces 
     where query.Where(x=> x.Name == c.Contact.Name).Count() > 0 
     select c); 

//Now query_2 contains all contactoperationsplaces which have a contact that was found in var query 

Al contrario, c'è un modo molto più semplice per fare questo, e questo è saltando la prima parte del tutto.

IQueryable<ContactOperationPlaces> query_2 = 
    (from c in _ctx.ContactOperationPlaces 
     where c.Contact.Name.ToLower().Contains(strName.ToLower()) 
     select c); 

Se si utilizza Entity Framework, non è necessario eseguire alcun join purché siano state definite associazioni tra le tabelle.

Ora che lo guardo, la mia seconda soluzione è molto più efficiente e più facile. Ma se avete bisogno di fare qualche altra elaborazione inbetween questi comandi, la soluzione si lavora troppo :)

Se avete bisogno di ulteriori spiegazioni, non esitate a chiedere :)

+0

Voglio mantenere il contatto che ha una connessione a un municipio specifico in spazi di contatto, quindi voglio un articolo per contatto. So che quest'ultima soluzione è la migliore, ma come ho spiegato nella mia domanda, non posso selezionare tutto in una volta. – linnkb

+0

queste diverse tabelle sono collegate tramite un'associazione uno-a-molti? Se è così, questa è la soluzione migliore. Se hai altri join su cui si basa il contatto, dovresti sostituirli facendo qualcosa di simile come sopra. Dato che EF fa tutto ciò che ti unisce e devi solo dire quello che vuoi, penso che l'unione manuale delle tabelle con EF sia inefficiente di default (a parte forse alcune situazioni molto insolite). Non incolpare te, ma ti suggerirei di evitare i join manuali in quanto dovrai fare più lavoro, con meno feedback/debugging ... – Flater

Problemi correlati