2011-08-25 8 views
7

Ho un elenco esterno di SharePoint che punta a una tabella SQL di record di 100.000. Devo impostare un filtro sull'operazione Elenco di lettura altrimenti l'elenco non funziona. Timeout durante il tentativo di restituire l'elenco completo. Quindi ho aggiunto un filtro Limite di dimensione 200 all'operazione.Interrogare l'elenco esterno di grandi dimensioni con CAML

Il problema è che quando eseguo una query sull'elenco esterno utilizzando CAML, esso cerca solo le 200 voci restituite, non l'elenco completo.

Mi piacerebbe che esegua ricerche nell'intero elenco, ma restituisca solo un massimo di 200 voci corrispondenti.

Come posso ottenere il massimo?

+0

Avete BCS come connettore SQL o connettore Assembly? –

+0

@MaksMatsveyeu Sono interessato a questa domanda in senso generale, cioè dovrebbe essere agnostico al connettore. –

risposta

2

Forse questa risposta ad SharePoint-Exchange può aiutarti. https://sharepoint.stackexchange.com/questions/31566/caml-and-external-list-pass-parameter-to-readlist-finder-method

mio idead è che si avrà probabilmente bisogno di modificare il vostro readlist-metodo per fare in modo che legga l'intera SQL-tavolo, ma con l'Where-parametro specificato dal parametro filtro nel readlist-metodo. Qualcosa di simile

Pseudo codice:

public static IEnumerable<YourEntity> ReadList(string param) 
{ 
    if(string.IsNullOrEmpty(param) == true) 
    { 
     //Your original code thata only fetches the first 200 items 
    } 
    else 
    { 
     //Read your SQL-table with a Where ParamName = 'param' - clause 
    } 
} 

Buona fortuna

+0

Questa è una soluzione ragionevole, ma non ottimale. Il consumatore deve ancora avere una conoscenza decente dell'implementazione del sistema esterno per capire come interrogarlo. Idealmente, una lista esterna potrebbe essere interrogata semplicemente come una lista. –

+0

Grazie per l'input. Lavori ad Avega a Göteborg? –

+0

Nessun problema. Sì, lo faccio davvero. Conosci noi? Vivi in ​​Svezia? –

-1

Utilizzare la proprietà ListItemCollectionPosition da SPQuery e SPListItemCollection ad es.

using (var web = site.OpenWeb("bla-bla")) 
{ 
    var list = web.Lists["your_list"]; 
    var query = new SPQuery(); 
    query.Query = "your query"; 
    do 
    { 
    var items = list.GetItems(query); 
    foreach(SPListItem item in items) 
    { 
     //your code 
    } 
    query.ListItemCollectionPosition = items.ListItemCollectionPosition; 
    } while(query.ListItemCollectionPosition != null); 
} 
+0

Questo non funziona. Dopo la prima ricerca (in cui si cercano solo i primi 200 documenti come specificato dal filtro limit), ListItemCollectionPosition è nullo, quindi non esegue la ricerca nei successivi 200 record ... – Mort

+0

Provare ad aggiungere query.ViewAttributes = "Scope = 'RecursiveAll' "; e query.Folder = list.RootFolder; – MishaU

2

Sulla base della struttura della query e le informazioni presentate qui, reports indicate<RowLimit> implementa le funzionalità che desiderate:

Il L'elemento RowLimit imposta il limite di riga per una vista.

Sintassi

Attributi

  • paging: booleano opzionale. VERO se l'elenco supporta visualizzazione di più elementi pagina per pagina. Se FALSE o non specificato, il limite di riga è assoluto e non vi è alcun collegamento per visualizzare più elementi.

Avvertenze: nota le osservazioni in the documentation.

Probabilmente avete già ispezionato questo per i vostri scopi (citando la tua domanda: "Così ho aggiunto un filtro limite di dimensione 200 sulla operazione".). Quindi, al prossimo numero:

Il problema che ne consegue è che quando interrogo l'elenco esterno utilizzando CAML si cerca solo le 200 voci restituite, non l'elenco completo.

Questo sembra strano. Se si utilizza effettivamente <RowLimit> e la documentazione è corretta:

L'elemento RowLimit imposta il limite di riga per una vista.

And:

Il tag <RowLimit> è nella definizione dello schema di una vista (figlio diretto di) e quindi non può essere nidificato all'interno di un tag <Query>.

Allora dovrebbe tenere vero che la query nidificate esegue prima vista componente per soddisfare la garanzia della sua dichiarazione limite. Come corollario di ciò, questo dovrebbe consentire di eseguire il paging dei risultati attraverso il resto del set definito dalla query.

Sulla base di questi principi, si potrebbe creare una query di paging come questo:

<View> 
    <RowLimit Paged='True'>200</RowLimit> 
    <Method Name='ReadList'/> 
    <Query> 
     <Where> 
      <Contains> 
        <FieldRef Name='Name'/> 
        <Value Type='Text'>{0}</Value> 
      </Contains> 
     </Where> 
    </Query> 
    <ViewFields> 
    <FieldRef Name='Name'/> 
    <FieldRef Name='Id'/> 
    <FieldRef Name='BdcIdentity'/>       
    </ViewFields> 
</View> 

Rilevando, come accennato nella documentazione, che dobbiamo attuare <PagedRowset>. Se questo non è desiderato, abbiamo impostato Paged='FALSE' sopra.

Sono probabilmente completamente fuori base qui, perché questo sembra esattamente quello che hai già provato. Ma, nell'interesse di una mappatura esaustiva dello spazio, non può far male suggerirlo.

+0

Per un elenco esterno, è chiaro che nelle condizioni di implementazione "normali", la query non viene passata al connettore per la traduzione. Quindi sembra che il comportamento sia quello di chiedere al connettore di fornire il maggior numero possibile di elementi, e quindi al risultato vengono applicate la query di filtro e le condizioni di riga. –

+0

@RexM concordato. Il percorso verso http://sharepoint.stackexchange.com, [questa risposta] (http://sharepoint.stackexchange.com/a/10120) discute alcune idiosincrasie di ''/'' ordine di precedenza nell'API SOAP che appaiono pertinenti anche a CAML. Naturalmente, il modo migliore per gestirlo sarebbe specificare [una query da eseguire internamente da Sharepoint anziché dal componente Web] (http://msdn.microsoft.com/en-us/library/ee557243.aspx), ma suppongo che la possibilità di farlo sia limitata per motivi amministrativi. Non sono sicuro di quale sia la causa del problema senza ulteriori informazioni. – MrGomez

0

Sfortunatamente, questo è un problema noto con l'interrogazione di un elenco esterno. Tuttavia, nelle web part OOB, il paging è supportato tramite XSLT. RowLimit funziona solo in XSLT e non nella query CAML. Nell'Elenco esterno, non vi è alcun paging lato server, piuttosto il paging è lato client, il che significa che SharePoint estrae tutti i dati e imposta un limite Filtro nella vista.

0

Questo è uno degli scenari in cui il BCS senza codice proprio non lo taglia. Implementalo come stored procedure sul server del database o utilizzando un connettore BDC personalizzato incorporato in Visual Studio.

+0

Non funziona nemmeno con un connettore BCS personalizzato. "Nessun codice" non ha nulla a che fare con questo. Non è ancora possibile rinviare l'esecuzione della query al connettore. Spero di avere qualche pensiero fuori dagli schemi per buone alternative. –

0

Se non è possibile inserire l'intera tabella SQL nell'elenco esterno, non è possibile interrogare quel set di dati come si potrebbe fare in un elenco di SharePoint.

Tuttavia, posso offrire una soluzione che abbiamo utilizzato per scenari praticamente identici a questo che ha funzionato molto bene per noi. Nel nostro scenario stiamo interrogando un database Oracle che impiega sempre per restituire insiemi di dati di grandi dimensioni.

L'approccio che abbiamo adottato è stato quello di utilizzare il modello di fabbrica per determinare in base a quale mezzo l'origine dati (elenco SharePoint, database esterno, ecc.) Dovrebbe essere interrogata.

Gli esempi seguenti sono un po 'banali, ma illustrano bene il concetto.

Quindi, iniziare con una interfaccia che definisce come i dati ottenuti non essere interrogato e quali campi sarà restituito:

public interface IQueryData 
{ 
    string ListToQuery { get; set; } 
    List<MyResultObject> ExecuteQuery(); 
} 

avreste un oggetto personalizzato che rappresenta un singolo record restituito dalla query

public class MyResultObject 
{ 
    public string FileRef { get; } 
    public string Title { get; set; } 
    // any other fields you'd like to see potentially returned... 
} 

Poi si avrebbe un fornitore di dati che implementa questa interfaccia per l'origine dati SQL

public class SqlDataProvider : IQueryData 
{ 
    public string ListToQuery { get { return "BigSqlTable"; } } 
    public List<MyResultObject> ExecuteQuery() 
    { 
     // query your external data source here... 
     // populate a list of MyResultObject's from the result set and return it to the consumer 
    } 
} 

Si piacerebbe anche avere un fornitore di dati che implementa l'interfaccia per un'origine dati di SharePoint

public class SharePointDataProvider : IQueryData 
{ 
    public string ListToQuery { get { return "MySharePointList"; } } 
    public List<MyResultObject> ExecuteQuery() 
    { 
     // query your SharePoint list here, using CAML, SharePoint object model, etc... 
     // populate a list of MyResultObject's from the result set and return it to the consumer 
    } 
} 

Con questa implementazione hai incapsulato la logica ed i dettagli della query nei vostri rispettivi fornitori di dati.

Ora che ci si dispone di una fabbrica che costruisce il provider appropriato di dati (in base al parametro ListToQuery specificato):

public static class QueryDataProviderFactory 
{ 
    public static IQueryData Build(string listToQuery) 
    { 
     switch(listToQuery) 
     { 
      case "BigSqlTable": return new SqlDataProvider(); break; 
      case "MySharePointList": return new SharePointDataProvider(); break; 
      // you can have many other implementations here that query your data sources in different manners 
     } 
    } 
} 

Infine, devi usare la vostra fabbrica per avviare la tua ricerca, passando il nome della sorgente di dati che si desidera eseguire una query:

public List<MyResultObject> RunQuery() 
{ 
    return QueryDataProviderFactory.Build("BigSqlTable").ExecuteQuery(); 
} 

Questo modello mantiene l'implementazione esterna incapsulata in un proprio fornitore di dati e astrae i dettagli della query da parte del consumatore. Tutto ciò che il consumatore deve fare è specificare il nome della lista che desidera interrogare e la Fabbrica decide quale implementazione avviare.

Si possono anche fare l'interfaccia IQueryData implementare generici per ulteriori estensibilità:

public interface IQueryData<T> 
{ 
    string ListToQuery { get; set; } 
    List<T> ExecuteQuery(); 
} 

Questo avrebbe aperto la porta per il consumatore di specificare anche il tipo di oggetto che ci si aspetta di essere restituito.

La nostra interfaccia dati di query ha in realtà molti più membri che aggiungono ulteriori punti di estensibilità ai nostri provider di query, ma ho pensato che questo esempio illustrasse il punto in modo conciso e facile da comprendere.

Volevo solo offrire questo suggerimento in quanto sembra quasi lo stesso scenario che abbiamo riscontrato circa un anno fa e questa strategia ha funzionato molto bene per noi.

+0

Sono contento che funzioni bene per te. In un sistema legacy (SP 2007), disponiamo anche di un provider LINQ interno che nasconde l'origine dati, ovvero SharePoint? È qualcos'altro? Il consumatore non ha bisogno di sapere. Ma ho messo una taglia su questa domanda perché nel mio attuale scenario, che ha esigenze radicalmente diverse, mi piacerebbe davvero che l'interfaccia di query fosse SharePoint stesso, non un codice personalizzato. –