从 DocumentDB 查询 POCO 实体

Querying POCO Entities from DocumentDB

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

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

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

我可以向文档添加一个类型字段,并且可以通过 WHERE type="user" 获取,但我不确定我不能 SELECT * FROM usersusers 作为文档类型(如果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);

文档 Db 存储库 class

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

由于 DocumentDB 没有针对每个文档的任何内置 type 元数据,您需要添加一个(例如您建议的 type 属性,或者任何其他区分 属性) 在同一集合中存储异构文档时,并在您的 WHERE 子句中使用它。你给这个 属性 起什么名字,以及你在其中存储什么值,与你的集合名称无关。

关于您的具体示例 SELECT * from users WHERE type='user' 可以,但是 SELECT * from users 可以 return 所有文件,无论类型如何。

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

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

我开始使用一个 Type 属性,它只采用内部类型名称(基 class "Entity" 中的 getter)

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

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

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

如果 -identifier- 标识一个组,那么多个记录将具有 "id" 的唯一值,并且范围查询很容易简单地查询 pkey 和迭代。

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

id 和 pkey 也可以很好地用作唯一索引 - 当您发现自己缺少关系 SQL :-)

时,这是一个受欢迎的功能

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

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

将我们的持久层交换到其他东西

文档 class 有很多我们几乎没有研究过的有趣方法。

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

给你一些想法。