【问题标题】:CosmosDB Entity with the specified id does not exist具有指定 ID 的 CosmosDB 实体不存在
【发布时间】:2019-10-11 20:25:01
【问题描述】:

我正在开发一个 ASP.NET Core MVC API 来调用 Azure Cosmos DB 中的资源。当我尝试对任何特定 ID 执行 GET 时,我收到 DocumentClientException:系统中不存在具有指定 ID 的实体。我可以确认该实体确实存在于系统中,并且连接成功,因为我可以成功执行其他方法和请求。分区键是 _id 。

在 Visual Studio 中使用断点进行调试,我可以看到在 API 中接收到正确 ID 的位置,但我无法确认它具体发送到 Azure 的位置

控制器方法:(ID字段是一个随机的数字和文本字符串)

        //controller is MoviesController decorated with [Route(api/[controller])]
    //sample GET is to localhost:port/api/Movies/5ca6gdwndkna99
        [HttpGet("{id}")]
        public async Task<MoviesModel> Get(string id)
        {
            MoviesModel movie = await _persistence.GetMovieAsync(id);
            return movie;
        }

数据处理方式:

public async Task<MoviesModel> GetMovieAsync(string Id)
        {

            string _id = Id;
            RequestOptions options = new RequestOptions();
            options.PartitionKey = new PartitionKey(_id);
            var documentUri = UriFactory.CreateDocumentUri(_databaseId, "movies", Id);
            Document result = await _client.ReadDocumentAsync(documentUri,options);

            return (MoviesModel)(dynamic)result;
        }

其他方法,比如获取所有电影的列表并返回表格都可以正常工作,因此我们可以排除网络问题

public async Task<List<MoviesModel>> GetMoviesAsync() 
{
var documentCollectionUri = UriFactory.CreateDocumentCollectionUri(_databaseId, "movies");

            // build the query
            var feedOptions = new FeedOptions() { EnableCrossPartitionQuery = true };
            var query = _client.CreateDocumentQuery<MoviesModel>(documentCollectionUri, "SELECT * FROM movies", feedOptions);
            var queryAll = query.AsDocumentQuery();

            // combine the results
            var results = new List<MoviesModel>();
            while (queryAll.HasMoreResults)
            {
                results.AddRange(await queryAll.ExecuteNextAsync<MoviesModel>());
            }

            return results;
        }


        public async Task<List<GenresModel>> GetGenresAsync()
        {
            await EnsureSetupAsync();

            var documentCollectionUri = UriFactory.CreateDocumentCollectionUri(_databaseId, "genres");

            // build the query
            var feedOptions = new FeedOptions() { EnableCrossPartitionQuery = true };
            var query = _client.CreateDocumentQuery<GenresModel>(documentCollectionUri, "SELECT * FROM genres", feedOptions);
            var queryAll = query.AsDocumentQuery();

            // combine the results
            var results = new List<GenresModel>();
            while (queryAll.HasMoreResults)
            {
                results.AddRange(await queryAll.ExecuteNextAsync<GenresModel>());
            }

            return results;
        }

【问题讨论】:

    标签: c# asp.net-core azure-cosmosdb


    【解决方案1】:

    首先,我建议重新审视一下你的 cosmosDb 设计,因为以下原因......

    问题:

    1. 如果你的 _id 是随机的数字和文本字符串,那就不好了 要将整个 _id 作为您的分区键,bcz 这将创建一个 每个条目的新分区。(尽管 azure 会对其进行范围分区 稍后)

    2. 仅通过分区键查询效率不高,用于精确定位 我们应该同时拥有分区键和行键的查询。

    解决方案

    1. 将 _id 的前一个或两个字母作为分区键。 (所以你的分区是有限的)。

    2. 将您的 _id 作为您的行键。

    3. 如果您的 _id = "abwed123asdf",那么您的查询应该是..

      RequestOptions 选项 = new RequestOptions();

      options.PartitionKey = new PartitionKey(_id.Substring(0,1));

      options.RowKey = _id;

    这样,您的查找将在分区和行键的帮助下精确指向所需的条目。 (节省大量 RU)

    请参阅文档以根据您的需要选择更好的分区键https://docs.microsoft.com/en-us/azure/cosmos-db/partitioning-overview

    【讨论】:

    • 你的回答让我有点困惑。问题 #2 存在于哪里? OP 正在尝试点读取 (ReadDocumentAsync()),并提前设置分区键。怎么这么低效?此外,分区键的选择确实是一个广泛的话题;不确定设置 ID 的前两个字符会有什么帮助;似乎他们仍然需要跨分区查询,尤其是在他们的查询搜索类型之类的内容时。
    • 我完全理解唯一值不应该是 partitionKey!这只是一个快速而肮脏的演示,“我能以多快的速度将工作 API 连接到 Cosmos DB”。收藏夹中目前总共只有 4 个文档。我非常感谢这个小费!尽管如此,我仍将一个已确认的现有 _id 传递给 ReadDocumentAsync() 并得到一个未找到实体的响应,我希望得到理顺:/
    • 另外,有趣的是,options.RowKey 不是有效的子属性 :shrug: 我使用了错误的包吗?它是 Microsoft.Azure.DocumentDb.Core 2.4
    • 仅供参考,我在 Github github.com/Azure/azure-cosmos-dotnet-v2/issues/711 上打开了一个问题
    【解决方案2】:

    我能够通过完全重构到 dotnet v3 SDK 来实现这一点。我的解决方案代码在comments of the gitHub link

    using Microsoft.Azure.Cosmos;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using VidlyAsp.DataHandlers;
    
    namespace VidlyAsp.DataHandlers
    {
        public class PersistenceNew
        {
    
            private static string _endpointUri;
            private static string _primaryKey;
            private CosmosClient cosmosClient;
    
            private CosmosDatabase database;
            private CosmosContainer movieContainer;
            private CosmosContainer genreContainer;
            private string containerId;
            private string _databaseId;
    
    
            public PersistenceNew(Uri endpointUri, string primaryKey)
            {
                _databaseId = "Vidly";
                _endpointUri = endpointUri.ToString();
                _primaryKey = primaryKey;
                this.GetStartedAsync();
            }
            public async Task GetStartedAsync()
            {
                // Create a new instance of the Cosmos Client
                this.cosmosClient = new CosmosClient(_endpointUri, _primaryKey);
                database = await cosmosClient.Databases.CreateDatabaseIfNotExistsAsync(_databaseId);
                CosmosContainer moviesContainer = await GetOrCreateContainerAsync(database, "movies");
                CosmosContainer genresContainer = await GetOrCreateContainerAsync(database, "genres");
                movieContainer = moviesContainer;
                genreContainer = genresContainer;
            }
    
            public async Task<GenresModel> GetGenre(string id)
            {
    
                var sqlQueryText = ("SELECT * FROM c WHERE c._id = {0}", id).ToString();
                var partitionKeyValue = id;
    
                CosmosSqlQueryDefinition queryDefinition = new CosmosSqlQueryDefinition(sqlQueryText);
    
    
                CosmosResultSetIterator<GenresModel> queryResultSetIterator = this.genreContainer.Items.CreateItemQuery<GenresModel>(queryDefinition, partitionKeyValue);
    
                List<GenresModel> genres = new List<GenresModel>();
    
                while (queryResultSetIterator.HasMoreResults)
                {
                    CosmosQueryResponse<GenresModel> currentResultSet = await queryResultSetIterator.FetchNextSetAsync();
                    foreach (GenresModel genre in currentResultSet)
                    {
                        genres.Add(genre);
    
                    }
                }
                return genres.FirstOrDefault();
            }
    
            public async Task<MoviesModel> GetMovie(string id)
            {
    
                var sqlQueryText = "SELECT * FROM c WHERE c._id = '" + id + "'";
                var partitionKeyValue = id;
    
                CosmosSqlQueryDefinition queryDefinition = new CosmosSqlQueryDefinition(sqlQueryText);
    
    
                CosmosResultSetIterator<MoviesModel> queryResultSetIterator = this.movieContainer.Items.CreateItemQuery<MoviesModel>(queryDefinition, partitionKeyValue);
    
                List<MoviesModel> movies = new List<MoviesModel>();
    
                while (queryResultSetIterator.HasMoreResults)
                {
                    CosmosQueryResponse<MoviesModel> currentResultSet = await queryResultSetIterator.FetchNextSetAsync();
                    foreach (MoviesModel movie in currentResultSet)
                    {
                        movies.Add(movie);
    
                    }
                }
                return movies.FirstOrDefault();
            }
    
            /*
        Run a query (using Azure Cosmos DB SQL syntax) against the container
    */
            public async Task<List<MoviesModel>> GetAllMovies()
            {
                List<MoviesModel> movies = new List<MoviesModel>();
    
                // SQL
                CosmosResultSetIterator<MoviesModel> setIterator = movieContainer.Items.GetItemIterator<MoviesModel>(maxItemCount: 1);
                while (setIterator.HasMoreResults)
                {
                    foreach (MoviesModel item in await setIterator.FetchNextSetAsync())
                    {
                       movies.Add(item);
                    }
                }
    
                return movies;
            }
    
            public async Task<List<GenresModel>> GetAllGenres()
            {
                List<GenresModel> genres = new List<GenresModel>();
    
                // SQL
                CosmosResultSetIterator<GenresModel> setIterator = genreContainer.Items.GetItemIterator<GenresModel>(maxItemCount: 1);
                while (setIterator.HasMoreResults)
                {
                    foreach (GenresModel item in await setIterator.FetchNextSetAsync())
                    {
                        genres.Add(item);
                    }
                }
    
                return genres;
            }
    
            private static async Task<CosmosContainer> GetOrCreateContainerAsync(CosmosDatabase database, string containerId)
            {
                CosmosContainerSettings containerDefinition = new CosmosContainerSettings(id: containerId, partitionKeyPath: "/_id");
    
                return await database.Containers.CreateContainerIfNotExistsAsync(
                    containerSettings: containerDefinition,
                    throughput: 400);
            }
        }
    }
    

    【讨论】:

    • 如果您编辑答案以在此处包含修改后的代码,那就太好了。这样一来,1) 这对其他人来说成为一个更有价值、可重用的答案,并且 2) 没有人需要离开现场查看您的代码。
    • 我们的想法是在此处发布相关代码和详细信息,以展示您如何解决问题。无需发布整个代码库。
    猜你喜欢
    • 2021-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    • 2014-04-03
    • 1970-01-01
    相关资源
    最近更新 更多