2012-03-16 15 views
13

Questo è LINQ to SQLPasso in un'espressione al di LINQ Select

Ho un sacco di classi diverse tutti facendo la stessa query, ma proiettando i risultati in modo leggermente diverso. Idealmente mi piacerebbe essere in grado di avere la query in un unico posto e far passare la proiezione nel metodo Select. Funziona bene per i tipi concreti:

public void GetResults() { 
    var junk = db.SiteProducts.Select(Project()); 
} 

public Expression<Func<DbEntities.SiteProduct, string>> Project() { 
    return p => p.ProductAlert; 
} 

Ma quando cerco di restituire un tipo anonimo, non riesce

public void GetResults() { 
    var junk = db.SiteProducts.Select(Project()); 
} 

public Expression<Func<DbEntities.SiteProduct, TResult>> Project<TResult>() { 
    return p => new { p.ProductAlert }; 
} 

Capisco perfettamente il motivo per cui l'inferenza di tipo generico sta fallendo nel secondo caso. Ma c'è un trucco, a corto di elaborare le mie stesse espressioni da zero? Mi manchi che possa farlo funzionare?

+0

Qualcosa di simile? http://stackoverflow.com/questions/1299534 –

+0

@Robert: quell'esempio sembra provare a scrivere un sostituto di Where, che è leggermente diverso. Ma potrei mancare qualcosa. Grazie comunque. –

+0

C'è anche questo: http://stackoverflow.com/questions/4683427, che potrebbe essere un po 'più vicino a quello che stai cercando di fare. –

risposta

0

Se ho capito bene la tua domanda è possibile utilizzare questo codice:

prima dichiarare un metodo per la selezione dei dati in questo modo:

public List<TResult> FindAll<TResult>(Func<Regions, TResult> selector) where TResult : class 
    { 
     using (RepositoryDataContext = new DataClasses1DataContext()) 
     { 
       return RepositoryDataContext.Regions.Select<Regions, TResult>(selector).ToList<TResult>(); 

     } 
    } 

allora si può costruire la vostra istruzione select in questo modo:

Func<Regions, SelectAllRegion> select = r => new SelectAllRegion 
     { 
      RegionID = r.RegionID, 
      RegionDescription = r.RegionDescription 
     }; 

mia SelectAllRegion:

public class SelectAllRegion 
{ 
    public SelectAllRegion() 
    { 
    } 
    public int RegionID { get; set; } 
    public string RegionDescription { get; set; } 
} 

e regione è Region tavolo in northwing.I spero che questo aiuto si

2

IdeaBlade ha una classe ProjectionSelector che è possibile utilizzare per astrarre le proiezioni. Quando è necessario costruire una query di proiezione ma non si conoscono i tipi coinvolti al momento della compilazione, è possibile creare un'istanza della classe ProjectionSelector e passare le informazioni sul tipo in fase di esecuzione.

La classe, e il codice di esempio, può essere trovato qui:

Creare dinamica "Select", "SelectMany" e clausole "GroupBy"
http://drc.ideablade.com/xwiki/bin/view/Documentation/dynamic-projection

+0

Grazie Robert. L'esistenza di questo progetto mi porta a credere che non ci sia zucchero sintattico che posso cospargere per farlo funzionare così com'è, ma lo lascerò aperto un po 'di più solo nel caso. –

+0

Possono emettere IL sotto il cofano. È così che fanno funzionare la loro classe di tipo anonimo in fase di runtime. –

+0

C'è anche questo affascinante bit di codice: http://msdn.microsoft.com/en-us/vstudio/bb737920#dynsel –

2

Questo non funzionerà di compilazione tempo. Usando cose dinamiche puoi farlo funzionare naturalmente.

Una soluzione semplice non consiste nell'utilizzare un tipo anonimo ma una classe DTO personalizzata. Una tale classe DTO richiede solo poche righe ed è facile da mantenere. Di solito questa è una buona soluzione.

2

Questa è una domanda interessante. Penso che un DTO possa aiutarti, ma ci sono limitazioni e insidie ​​a cui prestare attenzione. Prendiamo il seguente LINQPad Esempio:

class ProjectDTO 
{ 
    public string Name { get; set; } 

    public static Expression<Func<Project, ProjectDTO>> ToDTO = (e) => new ProjectDTO 
    { 
     Name = e.Name 
    }; 

    public ProjectDTO() {} 

    public ProjectDTO(Project project) 
    { 
     Name = project.Name; 
    } 
} 

void Main() 
{ 
    Projects.Select(p => p.Name).Dump(); 
    Projects.Select(ProjectDTO.ToDTO).Dump(); 
    Projects.Select(p => new ProjectDTO(p)).Dump(); 
} 

SQL generato:

SELECT [t0].[Name] 
FROM [Project] AS [t0] 
GO 

SELECT [t0].[Name] 
FROM [Project] AS [t0] 
GO 

SELECT [t0].[ProjectId], [t0].[Name], [t0].[Description], [t0].[DateCreated], [t0].[DateModified], [t0].[DateComplete], [t0].[CreatedBy] 
FROM [Project] AS [t0] 

Come si può vedere, non è possibile utilizzare un costruttore di copia per assegnare le proprietà del DTO come questo forza l'intero oggetto essere ritirato dal database.

Anche questo leggermente limitativo se si desidera estendere il DTO di base e aggiungere ulteriori proprietà per visualizzazioni più specializzate dei dati, il che significa che si potrebbe finire con più espressioni con codice simile.

Tuttavia, mi piace molto l'opzione due, ma sono sicuro che questa opzione è molto probabile limitato a proiezioni di tipo singolo, si consideri il seguente esempio:

var query = from p in Projects 
      join t in Tasks on p.ProjectId equals t.ProjectId 
      select ProjectDTO.ToDTO; //Can't be used like this 

Non credo che è possibile utilizzare l'espressione in questo tipo di sintassi di query. In generale, non penso che ci sarà una soluzione che funziona a tutto campo. Potrebbe essere necessario rivedere il progetto per vedere se è possibile fornire meno proiezioni, in base ad alcune delle proprietà che sono molto economiche da includere sempre nella query?

Senza usare la libreria Dynamic LINQ o costruendo manualmente l'albero delle espressioni, mi piacerebbe anche vedere se è possibile con LINQ-SQL/LINQ-Entities creare selezioni dinamiche.

Problemi correlati