2010-12-17 78 views
13

我正在使用Windows Azure表存儲並且有一個簡單的要求:添加一個新行,用該PartitionKey/RowKey覆蓋任何現有行。然而,保存更改總是拋出一個異常,即使我通過在ReplaceOnUpdate選項:添加或替換Azure表中的實體存儲

tableServiceContext.AddObject(TableName, entity); 
tableServiceContext.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate); 

如果實體已經存在,它拋出:

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> 

我真的不得不手動方式查詢現有排第一,並致電DeleteObject就可以了?這似乎很慢。當然有更好的方法?

回答

14

正如您看到的,你不能只添加具有相同的行鍵和分區鍵的另一個項目,所以你需要運行一個查詢來檢查,看看如果該項目已經存在。在這種情況下,我發現查看Azure REST API documentation以查看存儲客戶端庫可用的內容很有幫助。您會看到insertingupdating有單獨的方法。 ReplaceOnUpdate只有在更新時纔有效果,而不是插入。

儘管您可以刪除現有項目然後添加新項目,但您可以更新現有項目(節省一次往返存儲空間)。您的代碼可能是這個樣子:

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); 

編輯:雖然正確,在寫作的時候,與2011年9月更新微軟已經更新了Azure的表API包括兩個UPSERT命令,Insert or Replace EntityInsert or Merge Entity

+0

謝謝,我確實考慮過這個問題,但這意味着這段代碼必須知道需要複製的每個屬性(或者實體本身需要一個'CopyTo'方法),並且每當屬性更改時都需要更新它。我想我寧願支付刪除的費用並保證安全。 – EMP 2010-12-17 03:27:57

+0

好吧,我最終這樣做了,儘管我不想。無論我如何嘗試刪除並重新插入實體,它都無法可靠地工作 - 有一次它會成功,下一次失敗時會出現一個錯誤,下一次會失敗並返回另一個錯誤。 – EMP 2011-01-11 21:50:46

+0

我覺得很驚訝。你是否試圖保存添加和刪除作爲一個.Save()調用的一部分,或者你在做.AddObject(); 。保存(); .DeleteObject(); 。保存(); – knightpfhor 2011-01-12 02:37:01

1

爲了使用帶有ReplaceOnUpdate選項的Delete或SaveChanges操作不受TableContext管理的現有對象,您需要調用AttachTo並將該對象附加到TableContext,而不是調用指示TableContext試圖插入的AddObject它。

http://msdn.microsoft.com/en-us/library/system.data.services.client.dataservicecontext.attachto.aspx

+0

謝謝,但我不知道當我的方法被調用時實體是否已經存在或沒有。 – EMP 2010-12-17 02:55:36

+0

對不起,沒有仔細閱讀最初的問題。你需要事先閱讀。不過,不需要刪除那一行。如果該行存在,只需執行更新即可,如果不存在則插入。沒有避免它。 – Igorek 2010-12-17 03:10:04

1

在我的情況下,它是不允許先刪除它,所以我不喜歡這樣,這將導致一個交易服務器將首先刪除現有的對象,不是增加新的一個,除去需要複製的屬性值

 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; 
+0

我想你可能需要看看底下真正發生了什麼。據我所知,底層的REST API無法處理一次調用中的增加和刪除操作。所以,當你只是調用.Save()一次時,它會向存儲服務發送兩個調用。 – knightpfhor 2011-01-10 23:33:14

0

的存儲API不允許在一組交易每個實體多個操作(刪除+插入):

一個實體可以在交易中只出現一次,且僅一個操作可以針對它來進行。

看到MSDN: Performing Entity Group Transactions

所以其實你需要閱讀第一和決定的插入或更新。

0

Insert/Merge or Update已於2011年9月添加到API中。以下是使用Storage API 2.0的一個示例,它更易於理解1.7 api及更早版本的方式。

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 
       } 
      }); 
    } 
+0

這段代碼有一個小問題,就是樂觀併發。您可能會認爲「InsertOrReplace」的工作方式與「插入」和「替換」相同,因爲只要記錄自上次檢查以來沒有更改過,就會Upsert。但那會是錯誤的。 「InsertOrReplace」實際上意味着「無論如何覆蓋該記錄,並忽略樂觀併發檢查」。所以,如果你不關心樂觀的併發性,那麼太棒了!否則,您可能不希望將其用作「Upsert」。 – AndyJ 2013-10-08 15:56:31

相關問題