2011-12-14 11 views
10

Scusate se il titolo è fuorviante, non sapevamo come descriverlo.Applicare il selettore a chiave Linq Func <T, TResult> a livello di singolo elemento

Il mio obiettivo finale è quello di avere un metodo di estensione di IQueryable<T> e una qualche forma (vedi sotto per esempio) di espressione che mi permetterà di dover restituire un IQueryable<EntityIndex<T>> (o simile) che contiene l'originale T nel campo Entity e un array/numerico contenente gli elementi come descritto dallo una qualche forma di espressione.

So che in realtà non ha senso, speriamo che dopo un esempio ...

Questo è quello che ho finora:

class EntityIndex<T, TKey> 
{ 
    T Entity { get; set; } 
    // Doesn't have to be IEnumerable, whatever is easier 
    IEnuermable<TKey> Index { get; set; } 
} 
static class Elsewhere 
{ 
    [Extension()] 
    public IQueryable<EntityIndex<T, TKey>> IndexBy<T, TKey>(this IQueryable<T> source, Expression<Func<T, TKey[]>> indexSelector) 
    { 
     return source.Select(n => new EntityIndex<T, TKey> { 
      Entity = n, 
      Index = new T[] { n }.Select(indexSelector) 
     }); 
    } 
} 

Nota: È possibile che questo non lo fa compilare, è semplicemente lì per provare e mostrare quello che sto cercando di ottenere.

Ho usato lo standard di selezione , ma sub-ottimale, dovuto creare arbitrariamente una serie di T l'assegnazione alla proprietà 'indice' per essere in grado di applicare il selettore. Spero che una scelta migliore del parametro possa risolvere questo problema, ma forse no. Il problema principale è che questo non viene compilato quindi se c'è un piccolo aggiustamento che gli consentirà di funzionare, per me va bene, se riesci a capire il mio incomprensibile e capisco cosa sto cercando di fare, e capita di conoscere un modo migliore per farcela sarei molto riconoscente. Idealmente, ho bisogno che la soluzione venga compresa dal motore L2S, che non sono convinto quanto sopra sia grazie all'introduzione della classe EntityIndex, ma spero che sia trattata come una classe anonima.

EDIT:

Buon punto Damien, il quadro generale è probabilmente molto più facile da descrivere ...

voglio un metodo di estensione che accetta l'espressione, l'espressione deve descrivere quali campi sull'entità a indice, che verrà utilizzato dopo questa particolare espressione per consentire a un criterio (clausola where) di essere applicato ai campi selezionati.

Per farla breve, in un certo numero di punti nel codice abbiamo una stringa di ricerca con caratteri jolly.Se ho un EntityA con Property1, Property2, Property3, ecc, non è raro vedere codice come:

scritto a mano, scusate errori di battitura minori

public string[] WildcardSearch(string prefixText, int count) 
{ 
    string searchTerm = prefixText.Replace(wildcard, string.Empty); 
    if (prefixText.StartsWith(wildcard) && prefixText.EndsWith(wildcard)) { 
     return entitySet.Where(n => n.Property1.Contains(searchTerm) || n.Property2.Contains(searchTerm)).Select(n => n.Property3).ToArray(); 
    } else if (prefixText.StartsWith(wildcard)) { 
     return entitySet.Where(n => n.Property1.EndsWith(searchTerm) || n.Property2.EndsWith(searchTerm)).Select(n => n.Property3).ToArray(); 
     // you get the picture, same with EndsWith, no wildcards defaults to contains... 
    } 
} 

EDIT:

Ulteriori chiarimento - usando l'WildcardEarch sopra come esempio, quello che speravo era di essere in grado di avere un selettore come segue o simili:

Func<EntityA, IEnumerable<string>> indexSelector = n => new string[] { 
    n.Property1, 
    n.Property2 
}; 

// Alternatively, a ParamArray of keySelector might work? 
Func<EntityA, string>[] keySelectors = new Func<EntityA, string>[] { 
    n => n.Property1, 
    n => n.Property2 
}; 
.210

Data un'espressione adeguata descrive quali campi sull'entità di cercare, restituendo il IQueryable<EntitySearch<T>> come mostrato sopra, speravo di poter applicare un unico criterio, simile a:

Func<EntitySearch<T>, bool> criterion = n => false; 
if (wildcardIsContains) { 
    criterion = n => n.Values.Any(x => x.Contains(searchTerm)); 
} else if (wildCardIsStartsWith) { 
    criterion = n => n.Values.Any(x => x.Contains(searchTerm)); 
    //etc 
} 

Data l'estensione in cima che non riesco a mettermi al lavoro, e questa logica di criterio, dovrei essere in grado di prendere un IQueryable<T>, selezionare alcuni campi e applicare una ricerca jolly appropriata sui campi, restituendo finalmente IQueryable<T> di nuovo dopo aver aggiunto il filtro.

Grazie!

Si prega di commentare se avete bisogno di ulteriori informazioni/chiarimenti ...

EDIT: Fiera uno @subkamren e grazie per l'interesse. Alcuni esempi non generici potrebbero essere utili. Creerò qualcosa e li aggiungerò a breve. Per ora, qualche chiarimento basato sul tuo commento ...

Dato un IQueryable<Animal> Voglio un'estensione che mi permetta di selezionare i campi su Animale su cui intendo cercare/indicizzare. Ad esempio, Animal.Description, Animal.Species.Name ecc. Questa estensione dovrebbe restituire qualcosa di simile a IIndexedQueryable<Animal>. Questo è il problema che sto cercando di affrontare nella domanda sopra. L'immagine più ampia menzionata, che sarei eccezionalmente soddisfatto se si è disposti ad aiutare, è la seguente:

L'interfaccia IIndexedQueryable<T> a sua volta vorrei un'estensione per la quale potrebbe essere richiesto un termine di ricerca string. L'estensione dovrebbe risolvere i caratteri jolly all'interno del termine di ricerca, estendere l'IQueryable originale con il criterio necessario per eseguire una ricerca nei campi indicizzati e restituire uno IQueryable<T> di nuovo.

Apprezzo che questo potrebbe essere fatto in un unico passaggio, ma speravo di farlo in questo modo in modo che più avanti potrei esaminare l'aggiunta di un terzo metodo di estensione applicabile a IIndexedQueryable<T> permettendomi di eseguire una ricerca del testo libero con SQL Server. .. ^^ Hai qualche senso?

Questa è l'immagine più grande, questa domanda riguarda principalmente la possibilità di specificare i campi che intendo indicizzare in modo tale da poterli utilizzare successivamente come menzionato qui.

+2

Non sono sicuro di aver ancora capito - forse se potessi dare un esempio di come questo metodo sarebbe * usato *, potrebbe essere più chiaro (o se questo è parte di una soluzione per un problema più grande, forse specifica il problema più grande e vediamo se possiamo risolverlo in un modo migliore)? –

+0

@Damien_The_Unbeliever - giusto, ho aggiunto una sezione più grande, spero che questo aiuti a chiarire un po 'le cose? L'idea è che dato il metodo di estensione su cui sono bloccato e un altro che crea un'espressione sufficiente basata sulla posizione dei caratteri jolly, dovrei essere in grado di ridurre tutte le versioni duplicate di WildcardSearch con versioni generiche/espresse leggibili. Grazie! – Smudge202

+1

Convertito in C# per raggiungere un pubblico più ampio – Smudge202

risposta

4

Quindi qualcosa di simile:

public static IEnumerable<EntityIndex<T, Y>> IndexBy<T, Y>(this IEnumerable<T> entities, Func<T, Y> indexSelector) { 
    return entities.Select(e => new EntityIndex<T, Y> { Entity = e, IndexValue = indexSelector(e) }); 
} 

Notando che EntityIndex genericamente definire con il TIndexType (denominata Y qui) è importante perché non si sa in anticipo ciò che l'indice è.L'uso di un generico permette Y sia un censimento, quindi il seguente funzionerebbe come un selettore index:

// Assuming Animal has attributes "Kingdom", "Phylum", "Family", "Genus", "Species" 
// this returns an enumeration of EntityIndex<Animal, String[]> 
var animalsClassified = someAnimals.IndexBy(a => new String[] { a.Kingdom, a.Phylum, a.Family, a.Genus, a.Species }); 

EDIT (calcolata ulteriore dettaglio):

Utilizzando quanto sopra, è possibile raggruppare i risultati per unico valore di indice:

var animalClassifications = animalsClassified 
           .SelectMany(ac => ac.IndexValue.Select(iv => new { IndexValue = iv, Entity = ac.Entity })) 
           .GroupBy(kvp => kvp.IndexValue) 

quello che ho descritto qui, tra l'altro, è (una forma molto semplificata) l'algoritmo MapReduce come popolare da Google. Una forma distribuita della stessa viene comunemente utilizzata per l'identificazione di parole chiave nella ricerca di testo, in cui si desidera creare un indice di (termine di ricerca) -> (elenco di documenti contenenti).

+0

Non dovrebbe essere 'e => new EntityIndex {Entity = e, IndexValue = indexSelector (e)}' nel primo esempio di codice sopra riportato? – afrischke

+0

Sì, grazie per la cattura. Modificato di conseguenza. –

+0

Per qualche motivo non sono stato informato di questo @ChrisShain, grazie mille. Darò questo via! – Smudge202

Problemi correlati