3

I have one solution, the question is why this happens and if this is a good solution. Background: In Azure DocumentDb I have a managed hash partitioned a db with one collection. The CLR objects inherit from the Document class. I have put JsonProperty-attribute on all properties.

The query that doesn't work is this one:

var a = _client.CreateDocumentQuery<T>(_database.SelfLink)
                .Where(d => d.Id == id)
                .AsEnumerable()
                .FirstOrDefault();

It would always return null (and the doc is there, I see it in the portal). I've been wasting so much time now, and all that I could get working was this:

var a = _client.CreateDocumentQuery<Document>(_database.SelfLink, "SELECT * FROM c")
                    .AsEnumerable().Where(t => t.Id == id)
                    .FirstOrDefault();
var obj = JsonConvert.SerializeObject(b);
T parsed = JsonConvert.DeserializeObject<T>(obj);

How awful isn't that? Does anyone know why the framework isn't deserializing this for me, and why it doesn't find anything with the first example?

Update:

Actually, that above "solution" doesn't deserialize all properties.. I have a property Dictionary<Guid,Dictionary<string, string>> that does not deserialize into the CLR property. The object is of the CLR type according to intellisense, but it has a lot of base class info. It seems to be nested in eternity though. Can't really see what the type is but 6 levels up (I think it's the Resource base class) I find a private _propertybag with all the properties as JTokens/JProperties (I honestly don't know how to tell which one they are). So, there's json data there, and the data I need is in the actual object, it just didn't bind to the property. I've tried to use the .SetProperty() method on the Resource class, and that does work. But this should be deserialized and bound when getting from the DocumentClient, no? What am I doing wrong here?

2 months later: I was looking into this again, and it turns out that for one place in my code the above horrific workaround was still the only thing that could get my document. The reason though was to be found earlier in the chain. The parameter for that method was Expression<Func<TEntity,bool>>. My chain for calling this was

public T CreateIfNotExists<T>(Guid id) where T : IBaseDocument
{
    var id = ProduceDocId(typeof(T), id);
    var result = _repository.GetSingle<T>(r => r.Id == id);
    ...
}

and then

public T GetSingle<T>(Expression<Func<T, bool>> predicate) where T : IBaseDocument
    {
        ... // error handling ommitted
        T res = _client.CreateDocumentQuery<T>(_database.SelfLink)
            .Where(predicate)
            .AsEnumerable()
            .FirstOrDefault();
        return res;
    }

The parameter 'predicate' would evaluate to an anonymous method closure, i.e. showing up in the debugger with 'c__DisplayClass2' (explanation for name by Eric Lippert). Like so:

{s => (Convert(s).Id == value(FooNameSpace.BarClass+<>c__DisplayClass2`1[FooNameSpace.FooClass]).someId)}

This isn't evaluated correctly by documentdb, and it will always return null. If I take the actual id that the anonymous method closure evaluates to, and pass in instead, that gives me the document.

Community
  • 1
  • 1
right_there
  • 175
  • 1
  • 1
  • 11

2 Answers2

3

Solved! Very very simple answer, and it's all due to my ignorance and lack of correct research effort (because man, I have put in effort..).

Property setter, on a document-to-be, cannot be inaccessible, i.e. private or protected is not OK.

That's it.

2 months later: Actually, that was not it. See original question's last part.

right_there
  • 175
  • 1
  • 1
  • 11
  • Your research and hard work is really appreciated. Saved me from banging my head against the wall. Thanks! – Animesh Aug 20 '19 at 14:49
0

You shouldn't need to do deserialize reading the Document from DocumentDB. A function like this should do it for you(the class name in this function is different than yours):

public List<Candidate> GetCandidateById(int candidateId)
{
    var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey);
    Database database = client.CreateDatabaseQuery().Where(db => db.Id == DatabaseName).AsEnumerable().FirstOrDefault();
    DocumentCollection documentCollection = client.CreateDocumentCollectionQuery(database.CollectionsLink).Where(db => db.Id == DocumentCollectionName).AsEnumerable().FirstOrDefault();

    return client.CreateDocumentQuery<Candidate>(documentCollection.DocumentsLink).Where(m => m.CandidateId == candidateId).Select(m => m).ToList();
}

I have posted a sample for DocumentDB here:

http://koukia.ca/post/azure-documentdb-vs-sql-azure,-a-performance-comparison

Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
Aram
  • 5,537
  • 2
  • 30
  • 41
  • 1
    Problem is that I have a managed hash partitioned db, which means I can't search on collection links or document links, because documents are distributed over collections according to the hash computed from the "PartitionKeySource" property that I therefore have on all these objects. So the above suggestion can't be used in this case. – right_there Sep 06 '15 at 21:00