【问题标题】:Querying POCO Entities from DocumentDB从 DocumentDB 查询 POCO 实体
【发布时间】:2015-08-19 09:35:14
【问题描述】:

我正在关注来自 Microsoft 的 this blog post,正在测试 DocumentDB。

我创建了一个集合,并通过我的应用程序上的不同 POCO 类插入了 2 个文档。它创建了文档,但我无法将它们过滤回各自的 POCO 类。我意识到我正在查询所有集合,因此它显然是在检索存储在该集合中的所有文档。

在查询时区分文档以便我可以按类型单独查询它们的最佳方法是什么?

我可以在文档中添加一个类型字段并且可以通过WHERE type="user" 获取,但我不确定我不能在users 是文档类型的情况下执行SELECT * FROM users(如果 DocumentDB 中有这样的东西) ,而不是集合。

这是我创建文档的方式:

    var user1= new User()
    {
        UserTypeId = 0,
        UserName = "user1@hotmail.com",
        Password = "12345",
        PasswordSalt = "saltyPassword",
        UserStatusId = 1,
        ProfilePhotoKey = "KJSY"
    };
    await DocumentDBRepository<User>.CreateItemAsync(user1);

    var client = new Client()
    {
        ClientName = "client1",
        Secret = "rxPBsIVYya2Jg2ZHPNG8gL0P36TnutiBehvEFgk938M=",
        Title = "Administration Front End Application",
        ApplicationTypeId = 0,
        Active = false,
        RefreshTokenLifeTime = 60,
        AllowedOrigin = "http://localhost:8080",
        AllowedRoles = "admin"
    };
    await DocumentDBRepository<Client>.CreateItemAsync(client);

文档数据库存储库类

public static class DocumentDBRepository<T>
{
    //Use the Database if it exists, if not create a new Database
    private static Database ReadOrCreateDatabase()
    {
        var db = Client.CreateDatabaseQuery()
                        .Where(d => d.Id == DatabaseId)
                        .AsEnumerable()
                        .FirstOrDefault();

        if (db == null)
        {
            db = Client.CreateDatabaseAsync(new Database { Id = DatabaseId }).Result;
        }

        return db;
    }

    //Use the DocumentCollection if it exists, if not create a new Collection
    private static DocumentCollection ReadOrCreateCollection(string databaseLink)
    {
        var col = Client.CreateDocumentCollectionQuery(databaseLink)
                          .Where(c => c.Id == CollectionId)
                          .AsEnumerable()
                          .FirstOrDefault();

        if (col == null)
        {
            var collectionSpec = new DocumentCollection { Id = CollectionId };
            var requestOptions = new RequestOptions { OfferType = "S1" };

            col = Client.CreateDocumentCollectionAsync(databaseLink, collectionSpec, requestOptions).Result;
        }

        return col;
    }

    //Expose the "database" value from configuration as a property for internal use
    private static string databaseId;
    private static String DatabaseId
    {
        get
        {
            if (string.IsNullOrEmpty(databaseId))
            {
                databaseId = ConfigurationManager.AppSettings["database"];
            }

            return databaseId;
        }
    }

    //Expose the "collection" value from configuration as a property for internal use
    private static string collectionId;
    private static String CollectionId
    {
        get
        {
            if (string.IsNullOrEmpty(collectionId))
            {
                collectionId = ConfigurationManager.AppSettings["collection"];
            }

            return collectionId;
        }
    }

    //Use the ReadOrCreateDatabase function to get a reference to the database.
    private static Database database;
    private static Database Database
    {
        get
        {
            if (database == null)
            {
                database = ReadOrCreateDatabase();
            }

            return database;
        }
    }

    //Use the ReadOrCreateCollection function to get a reference to the collection.
    private static DocumentCollection collection;
    private static DocumentCollection Collection
    {
        get
        {
            if (collection == null)
            {
                collection = ReadOrCreateCollection(Database.SelfLink);
            }

            return collection;
        }
    }

    //This property establishes a new connection to DocumentDB the first time it is used, 
    //and then reuses this instance for the duration of the application avoiding the
    //overhead of instantiating a new instance of DocumentClient with each request
    private static DocumentClient client;
    private static DocumentClient Client
    {
        get
        {
            // change policy to ConnectionMode: Direct and ConnectionProtocol: TCP on publishing to AZURE
            if (client == null)
            {
                string endpoint = ConfigurationManager.AppSettings["endpoint"];
                string authKey = ConfigurationManager.AppSettings["authKey"];
                Uri endpointUri = new Uri(endpoint);
                client = new DocumentClient(endpointUri, authKey);
            }

            return client;
        }
    }


    /* QUERY HELPERS */
    public static IEnumerable<T> GetAllItems()
    {
        return Client.CreateDocumentQuery<T>(Collection.DocumentsLink)
            .AsEnumerable();
    }
    public static IEnumerable<T> GetItems(Expression<Func<T, bool>> predicate)
    {
        return Client.CreateDocumentQuery<T>(Collection.DocumentsLink)
            .Where(predicate)
            .AsEnumerable();
    }
    public static async Task<Document> CreateItemAsync(T item)
    {
        return await Client.CreateDocumentAsync(Collection.SelfLink, item);
    }
    public static T GetItem(Expression<Func<T, bool>> predicate)
    {
        return Client.CreateDocumentQuery<T>(Collection.DocumentsLink)
                    .Where(predicate)
                    .AsEnumerable()
                    .FirstOrDefault();
    }

    public static async Task<Document> UpdateItemAsync(string id, T item)
    {
        Document doc = GetDocument(id);
        return await Client.ReplaceDocumentAsync(doc.SelfLink, item);
    }

    private static Document GetDocument(string id)
    {
        return Client.CreateDocumentQuery(Collection.DocumentsLink)
            .Where(d => d.Id == id)
            .AsEnumerable()
            .FirstOrDefault();
    }
}

我想得到:

    var q = DocumentDBRepository<User>.GetAllItems().ToList();
    var t = DocumentDBRepository<Client>.GetAllItems().ToList();

q 应该只包含由

创建的用户文档
await DocumentDBRepository<User>.CreateItemAsync(user1);

并且 t 应该只包含那些被创建的客户文档

await DocumentDBRepository<Client>.CreateItemAsync(client1);

【问题讨论】:

    标签: c# azure azure-cosmosdb


    【解决方案1】:

    由于 DocumentDB 没有为每个文档提供任何内置的 type 元数据,因此在将异构文档存储在同一个集合,并在您的 WHERE 子句中使用它。你如何命名这个属性,以及你在其中存储什么值,与你的集合名称无关。

    关于SELECT * from users WHERE type='user' 的具体示例,SELECT * from users 将返回所有文档,无论类型如何。

    默认情况下,所有属性都已编入索引,包括您新形成的 type 属性,它使您可以有效地执行 WHERE 子句过滤,而无需进行集合扫描。

    【讨论】:

    • 我一发布问题就发现了feedback.azure.com/forums/263030-documentdb/suggestions/…。 Azure 团队表示,他们正在对集合内的文档进行分组。我现在将坚持手动类型区分。 :)
    • 现在我正在考虑将我的聪明数据(让在这个部门工作的员工,或这个家庭的孩子)存储到文档数据库中,并将有关实体的详细信息存储到表存储中。您对使用 documentDB 和表存储有什么看法?
    • @Azadrum - 3-collection-per-scale-unit 主题已过时,因为现在每个集合都是自己的比例单位。关于在 DocumentDB 之外存储数据:这应该是一个单独的问题,但是……基于意见的问题通常很快就结束了。也许想出一个您可以在该领域提出的客观、具体的问题?
    【解决方案2】:

    关于如何区分集合中的文档类型...

    我开始使用 Type 属性,它简单地采用内部类型名称(基类“实体”中的 getter)

    我的期望是我们会在查询时使用 Type 属性。

    然而,我们很快转而使用类型值作为每个实体类型的分区键的后缀(“pkey”再次继承自 Entity - 因为我们通过将所有内容存储在一个我们必须使用的集合中来节省 $一个跨所有文档类型的分区键属性名称)

    如果类型名称为“Thing”且只有一个,则“id”为“Thing”且 pkey 为“-identifier-|Thing”

    如果 -identifier- 标识一个组,那么多个记录将具有唯一的“id”值,并且范围查询很容易,只需在 pkey 上进行查询和迭代。

    类型名应该是pkey后缀,保证不减少读写分布

    id 和 pkey 也可以很好地用作唯一索引 - 当您发现自己缺少关系 SQL 时,这是一个受欢迎的功能 :-)

    关于 POCO - 我正在认真考虑放弃直接 POCO 操作,因为我们在驼峰式 json 序列化和 sql 查询方面遇到了很多麻烦。我们最终无法信任全局驼峰式设置 - 而是在所有字段上详尽设置 json 名称。

    我正在考虑使用一个内部 POCO,该 POCO 持续往返于 Document。 POCO getter 和 setter 通过 getAttributeValue() 和 setAttributeValue() 引用 Document 实例。然后我们可以通过 DI 将我们的持久层换成别的东西

    Document 类有很多有趣的方法,我们还没有研究过。

    将我们的 POCO 与持久性分离也是可取的。

    只是给你一些想法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-21
      • 2012-07-06
      • 2012-03-22
      • 2015-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多