共用方式為


數據記憶體

Azure DevOps 服務 |Azure DevOps Server |Azure DevOps Server 2022

Azure DevOps 延伸模組可以將使用者喜好設定和複雜的資料結構直接儲存在 Microsoft 提供的基礎結構上,以確保您的使用者資料安全並備份,就像其他組織和專案資料一樣。 這也意味著,對於簡單的資料儲存需求,您作為擴充功能提供者,無需設定、管理第三方資料儲存服務或付費。

有兩種方法可以與資料儲存服務互動:透過 REST API 或透過 Microsoft 提供的用戶端服務,這是 VSS SDK 的一部分。 我們建議擴充功能開發人員使用提供的用戶端服務 API,因為它們提供使用者友善的 REST API 封裝。

備註

正在尋找 Azure DevOps REST API? 請參閱最新的 Azure DevOps REST API 參考

如需 .NET 用戶端程式庫的相關資訊,請參閱 Azure DevOps 的 .NET 用戶端程式庫

您可以儲存什麼

此服務旨在讓您儲存和管理兩種不同類型的資料:

  • 設定:簡單的鍵值設定 (例如使用者偏好設定)
  • 文件:類似複雜物件(文件)的集合

集合是文件的索引容器。 文件是屬於集合的 JSON Blob。 除了一些保留的內容名稱之外,您還可以控制和管理這些文件的結構描述。

如何設定資料範圍

設定和文件集合的範圍可以限定為:

  • 專案集合:由安裝延伸模組之專案集合的所有使用者共用
  • 使用者:安裝延伸模組之專案集合的單一使用者

設定儲存

管理設定 getValue() 的兩種主要方法是和 setValue()

  • getValue() 接受字串鍵(以及範圍等其他選項)並返回 IPromise。 此 Promise 的解析值是與所提供索引鍵相關聯的值。
  • setValue() 接受字串索引鍵、值和其他選項 (例如 scope),並傳回 IPromise。 此承諾的已解析值是設定的更新值。

以下是如何設定值的範例:

        private async initializeState(): Promise<void> {
        await SDK.ready();
        const accessToken = await SDK.getAccessToken();
        const extDataService = await SDK.getService<IExtensionDataService>(CommonServiceIds.ExtensionDataService);
        this._dataManager = await extDataService.getExtensionDataManager(SDK.getExtensionContext().id, accessToken);

        this._dataManager.getValue<string>("test-id").then((data) => {
            this.setState({
                dataText: data,
                persistedText: data,
                ready: true
            });
        }, () => {
            this.setState({
                dataText: "",
                ready: true
            });
        });
    }

以下是如何擷取設定值的範例:

    // Get data service
    SDK.getService(SDK.getContributionId()).then(function(dataService) {
        // Get value in user scope
        dataService.getValue("userScopedKey", {scopeType: "User"}).then(function(value) {
            console.log("User scoped key value is " + value);
        });
    });

如果未指定,設定 scopeType 會儲存在專案集合層級,而且可以使用延伸模組存取該專案集合中的所有使用者。 以下是如何在專案集合層級設定設定值的範例:

    // Get data service
    SDK.getService(SDK.getContributionId()).then(function(dataService) {
        // Set value (default is project collection scope)
        dataService.setValue("someKey", "abcd-efgh").then(function(value) {
            console.log("Key value is " + value);
        });
    });

資料(文件集合)儲存

若要處理索引鍵值組以外的更複雜資料,您可以利用文件的概念對擴充功能的資料執行 CRUD 作業。 文件是 JSON blob,透過兩個特殊屬性進行增強:ID 和 __etag。 如果它們對擴充功能的資料模型至關重要,則可以使用者定義 ID,或者如果未指定,系統會產生它們。 這些 ID 在特定集合中必須是唯一的。 由於集合是指延伸模組的特定範圍和實例,因此表示相同的文件識別碼可以在不同的集合中重複使用。

下列文件作業可供使用:

  • 取得文件
  • 建立文件
  • 設定文件(建立或更新)
  • 更新文件
  • 刪除文件

也可以對集合執行單一作業:取得所有文件

依身分證件取得文件

使用集合中的識別碼取得文件非常簡單,如下列範例所示:

    // Acquire data service
    SDK.getService(SDK.getContributionId()).then(function(dataService) {
        // Retrieve document by id
        dataService.getDocument("MyCollection", "MyDocumentId").then(function(doc) {
            // Assuming document has a property named foo
            console.log("Doc foo: " + doc.foo);
        });
    });

此作業會嘗試從 “MyCollection” 集合擷取識別碼為 “MyDocumentId” 的文件。 如果沒有提供範圍,服務預設會使用範圍限定為此延伸模組的整個實例的集合。 如果此集合或具有指定識別碼的檔不存在,則會傳回 404 錯誤,延伸模組應該處理該錯誤。 傳回的文件是 JSON 物件,其中包含其所有屬性,以及資料儲存服務所使用的特殊 ID 和 __etag 屬性。

    // Get data service
    SDK.getService(SDK.getContributionId()).then(function(dataService) {
        // Get document by id
        dataService.getDocument("MyCollection", "MyDocumentId").then(function(doc) {
            // Assuming document has a property named foo
            console.log("Doc foo: " + doc.foo);
        });
    });

此呼叫會嘗試從集合 “MyCollection” 擷取識別碼為 “MyDocumentId” 的文件。由於未提供範圍,因此服務使用的集合會限定為此延伸模組之整個實例的預設值。 如果此集合不存在,或具有該識別碼的檔不存在,則會傳回 404,延伸模組應該處理。 傳回的文件是 JSON 物件,除了資料儲存服務所使用的特殊 ID 和 __etag 內容之外,還包含其所有自己的內容。

建立文件

若要建立新文件,請執行類似下列範例的呼叫:

    // Get data service
    SDK.getService(SDK.getContributionId()).then(function(dataService) {
        // Prepare document first
        var newDoc = {
            fullScreen: false,
            screenWidth: 500
        };

        dataService.createDocument("MyCollection", newDoc).then(function(doc) {
            // Even if no ID was passed to createDocument, one gets generated
            console.log("Doc id: " + doc.id);
        });
    });

如果具有提供名稱和範圍的集合尚不存在,則會在建立文件本身之前動態建立它。

如果提供的文件包含屬性 id ,則該值會用作文件的唯一 ID。 請注意,提供的 id 應限制為 50 個字元。 如果該欄位不存在,服務會產生 GUID,並包含在解析 Promise 時傳回的文件中。

如果集合中已存在另一個文件,其識別碼與文件上提供的識別碼相同,則作業會失敗。 如果所需的行為是在 ID 不存在時建立新文檔,但如果存在,則修改現有文檔,則應使用該 setDocument() 方法。

設定文件 (更新或建立)

setDocument() 函數執行「更新插入」操作 - 如果現有文檔的 ID 存在並匹配集合中的文檔,則它會修改現有文檔。 如果識別碼不存在或不對應於集合中的任何文件,則會將新文件新增至集合。

    // Get data service
    SDK.getService(SDK.getContributionId()).then(function(dataService) {
        // Prepare document first
        var myDoc = {
            id: 1,
            fullScreen: false,
            screenWidth: 500
        };

        dataService.setDocument("MyCollection", myDoc).then(function(doc) {
            console.log("Doc id: " + doc.id);
        });
    });

更新文件

updateDocument 函式要求正在變更的文件已位於集合中。 如果未提供識別碼,或提供的識別碼不對應至集合中的任何檔,則會擲回例外狀況。

以下是如何使用 update 的範例:

    // Get data service
    SDK.getService(SDK.getContributionId()).then(function(dataService) {
        var collection = "MyCollection";
        var docId = "1234-4567-8910";
        // Get document first
        dataService.getDocument(collection, docId, { scopeType: "User" }).then(function(doc) {
            // Update the document
            doc.name = "John Doe";
            dataService.updateDocument(collection, doc, { scopeType: "User" }).then(function(d) {
                // Check the new version
                console.log("Doc version: " + d.__etag);
            });
        });
    });

刪除文件

此函式會從提供的集合中刪除具有提供ID的文件。 如果集合不存在或文件不存在,則會傳回 404。

以下是範例用法:

    // Get data service
    SDK.getService(SDK.getContributionId()).then(function(dataService) {
        var docId = "1234-4567-8910";
        // Delete document
        dataService.deleteDocument("MyCollection", docId).then(function() {
            console.log("Doc deleted");
        });
    });

取得集合中的所有文件

下列範例會使用資料服務從「MyCollection」集合擷取所有文件,然後將文件數目記錄至主控台:

    // Get data service
    SDK.getService(SDK.getContributionId()).then(function(dataService) {
        // Get all document under the collection
        dataService.getDocuments("MyCollection").then(function(docs) {
            console.log("There are " + docs.length + " in the collection.");
        });
    });

此呼叫會擷取範圍集合中的所有文件,限制為 100,000 個文件。 如果集合不存在,則會傳回 404 錯誤。

進階

設定的儲存方式

此呼叫會 setDocument 封裝用戶端方法,為其提供多個資料片段。 如前所述,設定會在內部儲存為文件。 因此,基本文檔是動態生成的,其中文檔的 ID 是方法中 setValue() 給出的鍵。 文件還有兩個屬性。 屬性 value 會保留傳遞給方法的值,而屬性 revision 會設定為 -1。 雖然該 revision 屬性在“使用文檔”部分進行了更多闡述,但在設置上下文中,在文檔中設置 revision-1 表示我們不關心此設置文檔的版本控制。

因為設定是儲存為文檔的,所以我們需要提供一個集合名稱,指示文件的儲存位置。 為了簡單起見,使用方法時setValue()/getValue(),集合名稱一律是特殊名稱。$settings 上一個呼叫會在下列端點發出 PUT 要求:

GET _apis/ExtensionManagement/InstalledExtensions/{publisherName}/{extensionName}/Data/Scopes/User/Me/Collections/%24settings/Documents

要求承載如下列範例:

{
                "id": "myKey",
                "__etag": -1,
                "value": "myValue"
}

REST API

假設此程式碼片段是在設定值後執行的,您應該會看到包含文字「值為 myValue」的警示訊息。getValue 方法再次是 REST API 的包裝函式,會向下列端點發出 GET 要求:

GET _apis/ExtensionManagement/InstalledExtensions/{publisherName}/{extensionName}/Data/Scopes/User/Me/Collections/%24settings/Documents/myKey

e標籤

__etag資料儲存服務會使用此欄位來進行文件並行管理。 在儲存更新之前,服務會驗證目前儲存的文件的 是否__etag符合已更新文件的 。__etag 如果它們相符,則會 __etag 遞增,並將更新的文件傳回給呼叫端。 如果它們不匹配,則表示要更新的文件已過時,並拋出異常。 延伸模組寫入器負責正常處理此例外狀況,方法是擷取最新的 __etag 檔、合併變更,然後重試更新,或通知使用者。

對於某些類型的文件,可能不需要提供的並行層級,而最後獲勝的模型可能更合適。 在這種情況下,在編輯文件時,輸入 -1 作為值來 __etag 表示此功能。 前面提到的設定服務採用此模型來儲存設定和首選項。