从DocumentDB查询POCO实体

时间:2015-08-19 09:35:15

标签: c# azure azure-cosmosdb

我正在关注Microsoft this blog post测试DocumentDB。

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

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

我可以在文档中添加一个类型字段,可以通过WHERE type="user"获取,但我不确定SELECT * FROM users users 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); 是一种文档类型(如果有的话)在DocumentDB中这样的事情,而不是集合。

以下是我创建文档的方式:

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

文档Db存储库类

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

我想得到:

await DocumentDBRepository<User>.CreateItemAsync(user1);

q应仅包含由

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

和t应仅包含由

创建的客户文档
validate="tel"

2 个答案:

答案 0 :(得分:2)

由于DocumentDB没有为每个文档添加任何内置type元数据,因此您需要添加一个(例如您建议的type属性或任何其他区分属性)将异构文档存储在同一个集合中,并在WHERE子句中使用它。您为此属性命名的内容以及您在其中存储的值与您的集合名称无关。

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

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

答案 1 :(得分:1)

重新考虑如何区分集合中的文档类型......

我开始使用Type属性,它只使用了iternal类型名称(基类“实体”中的getter)

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

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

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

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

类型名称应该是pkey后缀,以确保不减少读写分配

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

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

我正在考虑使用内部POCO,它继续存在于文档中。 POCO getter和setter通过getAttributeValue()和setAttributeValue()引用Document实例。然后,我们可以通过DI

将我们的持久层交换为其他内容

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

将POCO与持久性脱钩也是可取的。

给你一些想法。