2012-10-18 4 views
7

Ho un evento comune nella mia applicazione in cui creo una query per ottenere tutte le entità dove partitionkey è costante ma rowkey deve essere all'interno di un intervallo lessicale (ad esempio solo le righe che iniziano con un certo prefisso):Query intervallo di archiviazione tabella Azure con condizione comunemente utilizzata racchiusa nel metodo

//query to get all entities in partition "KnownPartition" where RowKey starts with "Prefix_" 
CloudTableQuery<MyEntity> query = 
    (from e in tableServiceContext.CreateQuery<MyEntity>(tableName) 
    where e.PartitionKey == "KnownPartition" 
      && e.RowKey.CompareTo("Prefix_") > 0 
      && e.RowKey.CompareTo("Prefix`") <= 0 // ` is '_' + 1 
    select e).AsTableServiceQuery(); 

devo usare CompareTo perché le funzioni di stringa come StartsWith non sono supportati in questo tipo di interrogazione. Funziona, ma la condizione è difficile da leggere e ripetuta molto. Così, invece di scrivere un sacco di domande con questa condizione difficile da leggere, preferisco piace fare una funzione che "inline" è:

public static Boolean HasPrefix(this String rowKey, String prefix) 
{ 
    return rowKey.CompareTo(prefix + '_') > 0 && rowKey.CompareTo(prefix + '`') <= 0; 
} 

CloudTableQuery<MyEntity> query = 
    (from e in tableServiceContext.CreateQuery<MyEntity>(tableName) 
    where e.PartitionKey == "KnownPartition" && e.RowKey.HasPrefix("Prefix") 
    select e).AsTableServiceQuery(); 

Ma quando ho eseguito questo, ottengo una deroga Azure sulla mia funzione non supportata. C'è un modo per scrivere questo in modo che sia supportato? Dopotutto, sto usando la stessa identica condizione della query che ha funzionato, appena racchiusa in una funzione ...

risposta

10

Se si interrompe la condizione in una funzione, la funzione deve restituire un albero di espressioni anziché un valore booleano. Non so se la sintassi di query LINQ sosterrà questo, ma lo si può fare con la sintassi metodo come segue:

public static Expression<Func<MyEntity, bool>> HasPrefix(String prefix) 
{ 
    return e => e.RowKey.CompareTo(prefix + '_') > 0 && e.RowKey.CompareTo(prefix + '`') <= 0; 
} 

CloudTableQuery<MyEntity> query = 
    (from e in tableServiceContext.CreateQuery<MyEntity>(tableName) 
    where e.PartitionKey == "KnownPartition" 
    select e) 
    .Where(HasPrefix("Prefix")) 
    .AsTableServiceQuery(); 
+0

Molto bello, questo funziona. Grazie. Suppongo che potremmo semplicemente scrivere l'intera espressione come una catena di metodi piuttosto che la sintassi della query di linq per essere costanti. –

+0

Oh, a proposito, spero che questo non comporti una scansione completa ... Come in, le entità effettivamente trasferite da azzurro sono già state controllate per la condizione prefisso, giusto? –

+1

L'URI della richiesta di archiviazione Azure risultante è: https://foo.table.core.windows.net/tablename()?$filter=((PartitionKey eq 'KnownPartition') e (RowKey gt 'Prefix_')) e (RowKey le 'Prefisso% 60'). Quindi verranno restituite solo le righe che soddisfano tutte le condizioni. Inoltre, poiché la query specifica la chiave di partizione e un intervallo di chiavi di riga, non dovrebbe essere richiesta una scansione completa della tabella sul back-end. – Kevin

17

Ecco una generalizzazione della risposta di Kevin. Fa la stessa cosa, ma funziona per qualsiasi stringa di prefisso piuttosto che per il caso specifico che David ha posto nella domanda originale.

public static Expression<Func<MyEntity, bool>> HasPrefix(String prefix) 
{ 
    char lastChar = prefix[prefix.Length - 1]; 
    char nextLastChar = (char)((int)lastChar + 1); 
    string nextPrefix = prefix.Substring(0, prefix.Length - 1) + nextLastChar; 

    return e => e.RowKey.CompareTo(prefix) >= 0 && e.RowKey.CompareTo(nextPrefix) < 0; 
} 
+0

Grazie per aver fornito la risposta generalizzata! MS dovrebbe aver appena incluso questo bit nel processore di query di linq! –