我正在关注微软测试DocumentDB的this blog post .
我创建了一个集合,并在我的应用程序中通过不同的POCO类插入了2个文档 . 它创建了文档,但我无法将它们过滤回各自的POCO类 . 我意识到我正在查询所有集合,因此它显然正在检索存储在该集合中的所有文档 .
在查询时区分文档的最佳方法是什么,以便我可以按类型单独查询它们?
我可以在文档中添加一个类型字段,并且可以通过 WHERE type="user"
但我不确定 SELECT * FROM users
是否 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);
文档Db存储库类
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 回答
由于DocumentDB没有每个文档的任何内置
type
元数据,因此在将异构文档存储在同一个集合中时,您需要添加一个(例如您建议的type
属性或任何其他区别属性)并使用它在你的WHERE
条款中 . 您为此属性命名的内容以及您在其中存储的值与您的集合名称无关 .关于
SELECT * from users WHERE type='user'
的具体示例可行,但SELECT * from users
将返回所有文档,无论类型如何 .默认情况下,所有属性都被编入索引,包括新形成的
type
属性,这使您可以有效地执行WHERE子句过滤,而无需进行集合扫描 .重新思考如何区分集合中的文档类型......
我开始使用Type属性,它只使用了iternal类型名称(基类“Entity”中的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与持久性脱钩也是可取的 .
对你来说只是一些想法 .