Aggiungere o sostituire un'entità in Azure Storage Table
-
11-10-2019 - |
Domanda
Sto lavorando con Windows Azure Storage Table ed avere un requisito semplice: aggiungere una nuova riga, sovrascrivendo qualsiasi riga esistente con quella PartitionKey / RowKey. Tuttavia, salvare le modifiche sempre genera un'eccezione, anche se mi passate l'opzione ReplaceOnUpdate:
tableServiceContext.AddObject(TableName, entity);
tableServiceContext.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate);
Se l'entità esiste già getta:
System.Data.Services.Client.DataServiceRequestException: An error occurred while processing this request. ---> System.Data.Services.Client.DataServiceClientException: <?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<code>EntityAlreadyExists</code>
<message xml:lang="en-AU">The specified entity already exists.</message>
</error>
Devo davvero a interrogare manualmente per la riga esistente prima e call DeleteObject
su di esso? Che sembra molto lento. Sicuramente c'è un modo migliore?
Soluzione
Come hai trovato, non si può semplicemente aggiungere un altro elemento che ha la stessa chiave chiave di fila e la partizione, quindi sarà necessario eseguire una query per verificare se l'oggetto esiste già. In situazioni come questa lo trovo utile a guardare il Azure REST documentazione API per vedere cosa è disponibile per la libreria client di archiviazione. Vedrai che ci sono metodi distinti per inserimento e l'aggiornamento . Il ReplaceOnUpdate ha effetto solo quando si è l'aggiornamento, non inserendo .
Mentre si potrebbe eliminare l'elemento esistente e quindi aggiungere il nuovo, si può solo aggiornare quello esistente (risparmiando un andata e ritorno per lo stoccaggio). Il vostro codice potrebbe essere simile a questo:
var existsQuery = from e
in tableServiceContext.CreateQuery<MyEntity>(TableName)
where
e.PartitionKey == objectToUpsert.PartitionKey
&& e.RowKey == objectToUpsert.RowKey
select e;
MyEntity existingObject = existsQuery.FirstOrDefault();
if (existingObject == null)
{
tableServiceContext.AddObject(TableName, objectToUpsert);
}
else
{
existingObject.Property1 = objectToUpsert.Property1;
existingObject.Property2 = objectToUpsert.Property2;
tableServiceContext.UpdateObject(existingObject);
}
tableServiceContext.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate);
Modifica Mentre corrette al momento della scrittura, con il settembre 2011 aggiornamento di Microsoft hanno aggiornato la tabella API Azure per includere due upsert comandi, inserire o sostituire entità e Inserisci o Unisci entità
Altri suggerimenti
Al fine di operare su un oggetto esistente non gestiti dalla TableContext con eliminare o SaveChanges con opzioni ReplaceOnUpdate, è necessario chiamare AttachTo e allegare l'oggetto alla TableContext, invece di chiamare AddObject che istruisce TableContext per tentare di inserirla .
nel mio caso non è stato permesso di rimuoverlo prima, quindi lo faccio in questo modo, questo si tradurrà in una transazione al server che prima rimuovere l'oggetto esistente e che aggiungere nuova, eliminando necessità di copiare i valori delle proprietà
var existing = from e in _ServiceContext.AgentTable
where e.PartitionKey == item.PartitionKey
&& e.RowKey == item.RowKey
select e;
_ServiceContext.IgnoreResourceNotFoundException = true;
var existingObject = existing.FirstOrDefault();
if (existingObject != null)
{
_ServiceContext.DeleteObject(existingObject);
}
_ServiceContext.AddObject(AgentConfigTableServiceContext.AgetnConfigTableName, item);
_ServiceContext.SaveChangesWithRetries();
_ServiceContext.IgnoreResourceNotFoundException = false;
Inserisci / Merge o aggiornamento è stata aggiunta alle API nel settembre 2011. Ecco un esempio utilizzando lo Storage API 2.0 che è più facile da capire, allora il modo in cui viene fatto nella 1.7 api e precedenti.
public void InsertOrReplace(ITableEntity entity)
{
retryPolicy.ExecuteAction(
() =>
{
try
{
TableOperation operation = TableOperation.InsertOrReplace(entity);
cloudTable.Execute(operation);
}
catch (StorageException e)
{
string message = "InsertOrReplace entity failed.";
if (e.RequestInformation.HttpStatusCode == 404)
{
message += " Make sure the table is created.";
}
// do something with message
}
});
}
L'API di archiviazione non permette più di un'operazione per entità (delete + insert) in una transazione di gruppo:
Un'entità può apparire solo una volta nella transazione, e una sola operazione può essere eseguita contro di essa.
MSDN: realizzazione delle operazioni di entità del gruppo
Quindi, in realtà è necessario leggere prima e decidere di inserimento o aggiornamento.