Passare una funzione C# come argomento (delegate)
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
a questo punto va definita una funzione che accetta come parametro il delegate
che può essere richiamata passando una funzione anonima che rispetta la firma/delegato
Definisco il delegato ExecCodeBlock
creo il context per collegarmi
creo il metodo principale dove andrò a richiamare la funzione che gestrisce il conflitto di versione
definisco la funzione ExecuteQueryUpdateItemAsync che si occupa di gestire il retry in caso di fallimento
Per simulare un conflitto:
Bisogna prima di tutto definire la firma del metodo usato come parametro, tramite la keyword delegate
C#
public delegate void ExecCodeBlock();
C#
private static async Task ExecuteAsync(ExecCodeBlock fnUpdate)
{
....istruzioni prima...
// invoco la funzione passata
fnUpdate();
....istruzioni dopo...
}
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();
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;
}
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();
});
}
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.
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