Is there a way to cast the Microsoft.Azure.Documents.Document
object to my class type?
I've written an Azure Function class, with a CosmosDBTrigger
. The trigger receives an array of Microsoft.Azure.Documents.Document
. I like having that Document
class so that I can access the meta data about the record itself, but I also would like to interact with my data from my class type in a static way.
I see the JSON representation of my data when I call ToString
. Should I manually convert that JSON to my class type using Newtonsoft?
If you need to map your Document
to your POCO in the function then the easiest way to do that is what you suggested.
Call the document.Resource.ToString()
method and use DeserializeObject
from JSON.NET or the json library you prefer. JSON.NET is recommended however as Microsoft's CosmosDB libraries use it as well.
Your mapping call will look like this:
var yourPoco = JsonConvert.DeserializeObject<YourPocoType>(document.Resource.ToString())
While solution offered by Nick Chapsas works, I would like to offer a few better options.
Preferred solution - improve your model
First, if you are interested in the extra meta fields then you can always include the chosen properties into your data access model and they will be filled in. for example:
public class Model
{
public String id { get; set; }
public String _etag { get; set; }
//etc.
}
Then you can use the existing API for deserializing thats explicit and familiar to all. For example:
var explicitResult = await client.ReadDocumentAsync<Model>(documentUri);
Model explicitModel = explicitResult.Document;
If you want the next layer model (ex: domain model) to NOT have those storage-specific meta fields then you need to transform to another model, but that is no longer a cosmosDB-level issue and there are plenty of generic mappers to convert between POCOs.
This is the IMHO cleanest and recommended way to handing data access in cosmosDB if you work on strongly typed document models.
Alternative: dynamic
Another trick is to use dynamic as the intermediate casting step. This is short and elegant in a way, but personally using dynamic always feels a bit dirty:
var documentResult = await client.ReadDocumentAsync(documentUri);
Model dynamicModel = (dynamic)documentResult.Resource;
Alternative: read JObject
Another alternative is to read the document as NewtonSoft's JObject
. This would also include all the meta fields and you could cast it further yourself without all the extra hopping between string representations. Example:
var jObjectResult = await client.ReadDocumentAsync<JObject>(documentUri);
Model JObjectResult = jObjectResult.Document.ToObject<Model>();
Alternative: Document
+ JObject
at the same time
Should you really-really want to avoid the document level meta fields in model AND still access them then you could use a little reflection trick to get the JObject
from the Document
instance:
var documentResult = await client.ReadDocumentAsync(documentUri);
Document documentModel = documentResult.Resource;
var propertyBagMember = documentResult.Resource.GetType()
.GetField("propertyBag", BindingFlags.NonPublic| BindingFlags.Instance);
Model reflectionModel = ((JObject)propertyBagMember.GetValue(documentResult.Resource))
.ToObject<Model>();
Beware that the reflection trick is relying on the internal implementation details and it is not subject to backwards compatibility guarantees by library authors.