DocumentDb CreateDocumentQuery <t>返回非T

时间:2017-02-01 15:23:53

标签: azure-cosmosdb

我试图从特定对象类型的documentdb获取文档列表 -

 _client.CreateDocumentQuery<RuleSetGroup>(_collectionLink)
            .Where(f => f.SourceSystemId == sourceSystemId).AsEnumerable().ToList();

这会返回 RuleSetGroup 以外的类型的对象,只要它们具有与我传入的属性SourceSystemId匹配的属性。我理解这是documentdb的工作原理,有没有办法强制执行类型T所以只返回那些对象?

我正在使用自动类型处理:

 JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
                {
                    TypeNameHandling = TypeNameHandling.Auto
                };

3 个答案:

答案 0 :(得分:1)

我的存储库对您来说可能有点太多了,简短的回答是您可以返回.AsDocumentQuery()而不是.ToList()

public async Task<IEnumerable<T>> GetDocumentsAsync<T>(Expression<Func<T, bool>> predicate, int maxReturnedDocuments = -1,
        bool enableCrossPartitionQuery = true, int maxDegreeOfParallellism = -1, int maxBufferedItemCount = -1)
    {
        //MaxDegreeofParallelism default = 0, add -1 to let SDK handle it instead of a fixed 1 network connection
        var feedOptions = new FeedOptions
        {
            MaxItemCount = maxReturnedDocuments,
            EnableCrossPartitionQuery = enableCrossPartitionQuery,
            MaxDegreeOfParallelism = maxDegreeOfParallellism,
            MaxBufferedItemCount = maxBufferedItemCount
        };

        IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
        UriFactory.CreateDocumentCollectionUri(_databaseName, _collectionName), feedOptions)
        .Where(predicate)
        .AsDocumentQuery();

        List<T> results = new List<T>();
        while (query.HasMoreResults)
        {
            var res = await query.ExecuteNextAsync<T>();
            results.AddRange(res);
        }
        return results;
    }

您可以这样调用上述方法:

var ecsterConfigs = await repoBO.GetDocumentsAsync<EcsterPaymentConfig>(c => c.ValidTo == null && c.Type == type);

然后,当我“可能”更新文档时,我有一个包装器,以跟踪_Etag,如果在我再次写下文档之前文档上有另一个更新,它将会改变。

public class DocumentWrapper<DocumentType>
{
    public DocumentWrapper(Document document)
    {
        Value = (DocumentType)(dynamic)document;
        ETag = document.ETag;
        TimeStamp = document.Timestamp;
    }
    public DocumentType Value { get; set; }
    public string ETag { get; set; }
    public DateTime TimeStamp { get; set; }
}

答案 1 :(得分:1)

除非您实现类型模式(为每个类添加Type属性)并将其用作额外过滤器,否则您将获得不同的文档类型。

原因是因为您正在存储NoSQL文档,这显然可能具有不同的架构。 DocumentDB平等对待它们,它们都是文档;当你查询时,分离不同的文档类型是你的责任(因为只有你知道区别)。

如果您的文档类型都具有属性“客户端”(例如,订单和发票),并且您创建了具有该属性的查询但映射到一个类型(订单),您将获得与过滤器匹配的订单和发票因为它们是与查询匹配的文档。反序列化逻辑就在您的最终,而不是在DocDB中。

关于在DocDB上存储不同文档类型时的类型模式的

Here is an article(查看基本类型模式部分)。

这样的事可能会解决它:

public abstract class Entity
{
    public Entity(string type)
    {
        this.Type = type;
    }
    /// <summary>
    /// Object unique identifier
    /// </summary>
    [Key]
    [JsonProperty("id")]
    public string Id { get; set; }
    /// <summary>
    /// Object type
    /// </summary>
    public string Type { get; private set; }
}

public class RuleSetGroup : Entity
{
    public RuleSetGroup():base("rulesetgroup")

}

public class OtherType : Entity
{
    public OtherType():base("othertype")

}

 _client.CreateDocumentQuery<RuleSetGroup>(_collectionLink).Where(f => f.Type == "rulesetgroup" && f.SourceSystemId == sourceSystemId).AsEnumerable().ToList();

在应用其他过滤器之前,您可以将查询包装在将类型设置为Where子句的帮助程序上(在LINQ中,您可以毫无问题地链接Wheres)。

答案 2 :(得分:0)

@Granlund如何让GetDocumentsAsync返回DocumentWrapper实例,同时仍允许谓词查询Value的属性?

这是我想出来的,但也许你有更好的方法:

    [TestMethod]
    [TestCategory("CosmosDB.IntegrationTest")]
    public async Task AddAndReadDocumentWrapperViaQueryAsync()
    {
        var document = new Foo { Count = 1, Name = "David" };
        var response = await client.CreateDocumentAsync(documentCollectionUri, document);

        var id = response.Resource.Id;

        var queryResult = await GetWrappedDocumentsAsync<Foo>(f => f.Where(a => a.Name == "David"));

        foreach (var doc in queryResult)
        {
            Assert.AreEqual("David", doc.Value.Name);
        }
    }

    public class Foo
    {
        public int Count { get; set; }

        public string Name { get; set; }
    }

    public class DocumentWrapper<DocumentType>
    {
        public DocumentWrapper(Document document)
        {
            Value = (DocumentType)(dynamic)document;
            ETag = document.ETag;
            TimeStamp = document.Timestamp;
        }

        public DocumentType Value { get; set; }

        public string ETag { get; set; }

        public DateTime TimeStamp { get; set; }
    }

    public async Task<IEnumerable<DocumentWrapper<T>>> GetWrappedDocumentsAsync<T>(
        Func<IQueryable<T>, IQueryable<T>> query,
        int maxReturnedDocuments = -1,
        bool enableCrossPartitionQuery = true,
        int maxDegreeOfParallellism = -1,
        int maxBufferedItemCount = -1)
    {
        //MaxDegreeofParallelism default = 0, add -1 to let SDK handle it instead of a fixed 1 network connection
        var feedOptions = new FeedOptions
        {
            MaxItemCount = maxReturnedDocuments,
            EnableCrossPartitionQuery = enableCrossPartitionQuery,
            MaxDegreeOfParallelism = maxDegreeOfParallellism,
            MaxBufferedItemCount = maxBufferedItemCount
        };

        IDocumentQuery<T> documentQuery =
            query(client.CreateDocumentQuery<T>(documentCollectionUri, feedOptions)).AsDocumentQuery();

        var results = new List<DocumentWrapper<T>>();
        while (documentQuery.HasMoreResults)
        {
            var res = await documentQuery.ExecuteNextAsync<Document>();

            results.AddRange(res.Select(d => new DocumentWrapper<T>(d)));
        }

        return results;
    }