2015-05-22 32 views
5

Ho questo problema per qualche ora ma oggi ho raggiunto il punto di non ritorno e sto chiedendo il tuo aiuto.Creare un tipo anonimo a seconda delle diverse condizioni e convertirlo in una classe

Supponiamo che io ho questo codice:

var query = DbContext.Table.Where(x => x.z == true).Select(x => new { x.a, x.b, x.c }); 
foreach(var item in query) 
{ 
    // Do the work 
} 

io alla fine con questo tipo anonimo e tutto va bene. Ora salta fuori qualcosa che mi fa condizionare la clausola Dove, quindi devo fare una query diversa a seconda della condizione e questo è dove sono veramente frustrato:

if(something) 
{ 
    var query = DbContext.Table.Where(x => x.z == true && x.zz == false).Select(x => new { x.a, x.b, x.c }); 
    foreach(var item in query) 
    { 
     // Do the work 
    } 
} 
else 
{ 
    var query = DbContext.Table.Where(x => x.z == true).Select(x => new { x.a, x.b, x.c }); 
    foreach(var item in query) 
    { 
     // Do the work 
    } 
} 

perché il lavoro da fare nel foreach il ciclo è esattamente lo stesso e non dovrei doverlo ripetere.

Così, invece di lavorare con il tipo anonimo ho dichiarato una nuova classe e provato questo:

class MyQuery 
{ 
    public int a { get; set; } 
    public string b { get; set; } 
    public decimal? c { get; set; } 
} 

IQueryable<MyQuery> query = null; 

if(something) 
{ 
    query = (IQueryable<MyQuery>)DbContext.Table.Where(x => x.z == true && x.zz == false).Select(x => new { x.a, x.b, x.c }); 
} 
else 
{ 
    query = (IQueryable<MyQuery>)DbContext.Table.Where(x => x.z == true).Select(x => new { x.a, x.b, x.c }); 
} 

foreach(var item in query) 
{ 
    // Do the work 
} 

Ma ora ho un 'System.InvalidCastException' gettati perché sto ancora ricevendo un tipo anonimo e apparentemente si può' essere gettato nella mia classe

Voglio evitare la ripetizione del codice per motivi di manutenzione ma non so come farlo. Sento che mi manca qualcosa di essenziale qui, ma non riesco a trovare quello che il tuo aiuto è il benvenuto e apprezzato.

Grazie mille per il vostro aiuto.

+0

Il secondo codice funzionerebbe ancora con la classe anonima (è sufficiente rimuovere il cast). L'unica volta che ti serve una classe personalizzata è quando vuoi trasferire i dati dentro o fuori un metodo. – juharr

risposta

9

Stai overcomplicating esso:

IQueryable<MyQuery> query = DbContext.Table; 

if (something) 
{ 
    query = query.Where(x => x.z == true && x.zz == false) 
} 
else 
{ 
    query = query.Where(x => x.z == true); 
} 

var result = query.Select(x => new { x.a, x.b, x.c }); 

foreach (var item in result) 
{ 
    // Do the work 
} 

Il metodo .Where() non cambia il "tipo" di query!

Nota anche che questo è legale:

var query = DbContext.Table.Select(x => new { x.a, x.b, x.c }); 

if (something) 
{ 
    query = DbContext.Table.Where(x => x.z == true && x.zz == false).Select(x => new { x.a, x.b, x.c }); 
} 
else 
{ 
    query = DbContext.Table.Where(x => x.z == true).Select(x => new { x.a, x.b, x.c }); 
} 

foreach (var item in query) 
{ 
    // Do the work 
} 

Sto sfruttando il fatto che i tipi anonimi con gli stessi parametri (stesso nome dei parametri, lo stesso tipo di parametri, lo stesso numero di parametri) all'interno del lo stesso assembly è "unificato" dal compilatore C#. Io uso questa linea var query = DbContext.Table.Select(x => new { x.a, x.b, x.c }); solo per "dare" il tipo alla variabile var. Quindi sovrascrivo completamente la query con altre query, perché i vari oggetti .Select(x => new { x.a, x.b, x.c }); "producono" dello stesso tipo (anonimo).

+0

Sì, stavo complicando troppo. Era il qualcosa di fondamentale che mi mancava. Molte grazie. – Francisco

2

È possibile modificare la condizione Where in modo che corrisponda a un caso o a un altro. In questo modo non dovrai più preoccuparti del codice duplicato o della creazione di un'altra classe che verrà utilizzata solo con questo metodo.

var query = DbContext.Table.Where(x => something && (x.z == true && x.zz == false) || !something && (x.z == true)).Select(x => new { x.a, x.b, x.c }); 
foreach(var item in query) 
{ 
    // Do the work 
} 

Oppure si può aggiungere un ternario confronto come @Rawling menzionato.

var query = DbContext.Table.Where(x => something ? (x.z == true && x.zz == false) : (x.z == true)).Select(x => new { x.a, x.b, x.c }); 
foreach(var item in query) 
{ 
    // Do the work 
} 
+0

@adricar Si noti che a volte il compilatore di query/ottimizzatore di SQL potrebbe essere "deragliato" con parti "morte" di 'where' ... – xanatos

+1

Mi sento' x => qualcosa? (x.z == true && x.zz == false): (x.z == true) 'è più chiaro qui. – Rawling

+1

@Rawling sei un genio: D, questo look più bello – adricadar