从DocumentDB查询POCO实体

3

我正在遵循微软的这篇博客文章测试DocumentDB:https://azure.microsoft.com/tr-tr/documentation/articles/documentdb-dotnet-application/

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

有什么最好的方法来区分查询中的文档,以便我可以按类型单独查询它们?

我可以在文档中添加一个类型字段,并通过WHERE type="user"进行获取,但我不确定是否可以使用SELECT * FROM users,其中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);
2个回答

2
由于DocumentDB没有为每个文档内置任何类型的元数据,因此当在同一集合中存储异构文档时,您需要添加一个(例如您建议的type属性或其他区分属性),并在WHERE子句中使用它。您给这个属性命名以及在其中存储什么值与集合名称无关。
关于您特定示例中的SELECT * from users WHERE type='user'将起作用,但SELECT * from users将返回所有文档,而不管类型如何。
默认情况下,所有属性都已索引,包括您新形成的type属性,这使得您可以有效地执行WHERE子句过滤,而无需进行集合扫描。

我在发布问题后立即发现了这篇文章http://feedback.azure.com/forums/263030-documentdb/suggestions/6345705-3-collections-per-capacity-unit-is-way-too-small。Azure团队表示他们正在努力将文档分组到集合中。目前,我将坚持手动类型区分。 :) - Azadrum
现在我正在考虑将我的智能数据(获取在该部门工作的员工或该家庭的孩子)存储到DocumentDB中,并将实体的详细信息存储到表存储中。您对使用DocumentDB和表存储有什么看法吗? - Azadrum
@Azadrum - 关于每个比例单位的三个集合主题已经过时,因为现在每个集合都是自己的比例单位。关于将数据存储在DocumentDB之外:那应该是一个单独的问题,但是...基于观点的问题通常很快就会被关闭。也许您可以提出一个客观、具体的问题? - David Makogon

1
关于如何区分集合中的文档类型...
我最初使用了一个Type属性,它仅采用了基类"Entity"中的内部类型名称(一个getter)。
我的期望是我们在查询时会使用Type属性。
然而,我们很快就开始使用类型值作为每个实体类型的分区键后缀(再次继承自Entity - 因为我们通过将所有内容存储在一个集合中来节省$,所以必须在所有文档类型上使用一个分区键属性名称)。
如果类型名称为“Thing”,并且只有一个,则“id”为“Thing”,pkey为“-identifier-|Thing”。
如果-identifier-标识一个组,则多个记录将具有唯一的"id"值,并且范围查询简单地查询pkey并进行迭代。
类型名称应该是pkey后缀,以确保您不会减少读取和写入分布。
id和pkey也可以作为唯一索引很好地工作 - 当您发现自己错过关系SQL时,这是一个受欢迎的功能 :-)
关于POCO - 我正在认真考虑放弃直接使用POCO操作,因为我们在驼峰命名的json序列化和sql查询方面遇到了很多麻烦。我们最终无法信任全局的驼峰设置,而是详尽地设置所有字段的json名称。
我正在考虑转向使用一个内部POCO,它会持久化到Document中。POCO的getter和setter通过getAttributeValue()和setAttributeValue()引用Document实例。然后我们可以通过DI将持久层切换到其他东西。
Document类有很多有趣的方法,我们几乎没有深入研究过。
解耦我们的POCO和持久化也是可取的。
这只是一些给你的想法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接