In C# è possibile passare una funzione come argomento ad un metodo.

Bisogna prima di tutto definire la firma del metodo usato come parametro, tramite la keyword delegate

C#

public delegate void ExecCodeBlock();
a questo punto va definita una funzione che accetta come parametro il delegate

C#

private static async Task ExecuteAsync(ExecCodeBlock fnUpdate)
{
    ....istruzioni prima...

    // invoco la funzione passata
    fnUpdate();

    ....istruzioni dopo...
}
che può essere richiamata passando una funzione anonima che rispetta la firma/delegato

C#

await ExecuteAsync(() =>
{
    ...istruzioni...
});

Esempio

Posso ad esempio creare un metodo per C# CSOM SharePoint per gestire un retry in caso di conflitto di salvataggio sull'update di un item.

Definisco il delegato ExecCodeBlock

C#: Delegate

public delegate void ExecCodeBlock();
creo il context per collegarmi

C#: Context

using Microsoft.SharePoint.Client;
// Microsoft.SharePointOnline.CSOM
// PnP.Framework

private static ClientContext GetClientContext(string siteUrl, string clientId, string clientSecret)
{
    ClientContext ctx = new PnP.Framework.AuthenticationManager()
        .GetACSAppOnlyContext(siteUrl, clientId, clientSecret);

    return ctx;
}
creo il metodo principale dove andrò a richiamare la funzione che gestrisce il conflitto di versione

C#: Main

string siteUrl = "https://tenantName.sharepoint.com/sites/xxx";
string clientId = "57a3...3d6d246";
string clientSecret = "kLR...Tk6k=";

using (ClientContext ctx = GetClientContext(siteUrl, clientId, clientSecret))
{
    List list = ctx.Web.GetList("/sites/xxx/lists/listName");
    // leggo l'item da aggiornare
    ListItem item = list.GetItemById(5);
    ctx.Load(item);
    // await ctx.ExecuteQueryRetryAsync(); // attivare solo per debug, vedi nota alla fine

    // richiamo il metodo passando la funzione anonima
    await ExecuteQueryUpdateItemAsync(ctx, item, () =>
    {
        item["Title"] = $"Test {DateTime.Now}";
        item.Update();
    });
}
definisco la funzione ExecuteQueryUpdateItemAsync che si occupa di gestire il retry in caso di fallimento

C#: Gestione conflitto

private static async Task ExecuteQueryUpdateItemAsync(ClientContext ctx, ListItem item, ExecCodeBlock fnUpdate)
{
    const int MAX_RETRY = 10;  // numero massimo di tentativi
    int retry = -1;
    do
    {
        try
        {
            // invoco la funzione con gli update da eseguire
            fnUpdate();

            // scrivo gli update su SharePoint
            await ctx.ExecuteQueryRetryAsync();

            // se l'update è andato a buon fine esco dal ciclo
            retry = -1;  
        }
        catch (Microsoft.SharePoint.Client.ServerException ex)
        {
            // TODO: log exception
            retry++;
            if (retry >= MAX_RETRY || ex.ServerErrorTypeName != "Microsoft.SharePoint.Client.VersionConflictException")
                // se ho raggiunto il numero massimo di tetativi
                // oppure l'eccezione non è del tipo "Version conflict"
                // esco con errore
                throw;
            }
            // se ho avuto un conflitto di salvataggio, 
            // devo ricaricare l'item con la nuova versione aggiornata
            // prima di riprovare a salvarlo
            item.RefreshLoad();
            await ctx.ExecuteQueryRetryAsync();
            // continuo con il do/while, riprovo a salvare
        }
    } while (retry != -1);
}
In questo caso alla funzione, oltre al delegato, passo anche i parametri ClientContext e ListItem SharePoint.
La tecnica di ricaricare l'item in caso di conflitto di versione è solo una delle strategie possibili per gestire questi conflitti.

Non è detto che vada bene in ogni caso in quanto si rischia di sovrascrivere i dati precedenti aggiornati da altri.
Se applicare o meno questa strategia, va valutato caso per caso.

Nota

Il metodo ExecuteQueryRetryAsync commentato dopo la load, va usato solo per simulare il conflitto di versione.

Per simulare un conflitto:
  • metti un breakpoint dopo l'ExecuteQueryRetryAsync
  • metti un beask point nel metodo ExecuteQueryUpdateItemAsync nel catch
  • modifica a mano un qualsiasi campo dell'item SharePoint
  • continua l'esecuzione
Tags:
C#236 SharePoint498 SharePoint Online75 SharePoint 2013137 CSOM9
Potrebbe interessarti anche: