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.
You can simply do a .ToString() in the Microsoft.Azure.Documents.Document class.
This class inherits from the Microsoft.Azure.Documents.JsonSerializable class that overrides the .ToString() method.
Here below is an example of deserializing the Document class to my Car.cs POCO using the new high-performant System.Text.Json Namespace:
Car car = JsonSerializer.Deserialize<Car>(document.ToString());
Related
I am working with OData V3, and i came across this line:
var people = await client.For<People>().FindEntriesAsync();
In order for this to work, I need to define the People class, which has a bunch of set/get, which represent the fields I want.
Is there a simple and convenient way to avoid hard coding the structure classes? I want to be able to issue that command without having to define a specific "People" class. I would much rather have my results be in dictionary form, were the key's would be the column name, and the value would be the value in that column for the specific object.
Simple.OData.Client supports untyped scenarios, in which the CLR class is unnecessary.
For example:
var people = await client.For("People").FindEntriesAsync();
Then, people is a dictionary instance that you can use IDictionary<string, object> to refer. Thanks.
I don't think there is a way to avoid define a class like People, but if you really want to do this, you can try un-typed feature in OData.
Simple.OData.Client supports typed, untyped and dynamic scenario. So you can rewrite your query like this:
var x = ODataDynamic.Expression;
var people = await client.For(x.People).FindEntriesAsync();
here is another example:
var person = await client
.For(x.People)
.Filter(x.FirstName == "John")
.Select(x.LastName)
.FindEntryAsync();
If I have a dictionary,
e.g. var results = (IDictionary<string, object>)fb.Get("me");
And that dictionary contains primitives, arrays and dictionaries of data can it be passed as a model directly? How does one override a mapping of a particular entry, say I want the date to be stored as a string formatted in a certain way.
Atm I'm doing the custom mapping in the Controller, but that has already started to become messy having all parsing code in the one place.
The alternative is I pass the dictionary in to the constructor of the model, and do the class specific parsing there..
Create a new class for the parsing logic rather than complicate your controller or your model. This parsing service class would contain all the messy parsing logic. This will keep your controller and model much cleaner.
var parsingService = new ParsingService(tb.Get("me"));
var model = parsingService.GetModel();
I load types dynamically through reflection, instantiate the classes, fill them with data and then save them to RavenDB. They all implement an interface IEntity.
In the RavenDB UI, I can see the classes correctly displayed and the meta data has the correct CLR type.
Now I want to get the data back out, change it and then save it.
What I'd like to do
I have the System.Type that matches the entity in RavenDB's CLR meta data, assuming that's called myType, I would like to do this:
session.Query(myType).ToList(); // session is IDocumentSession
But you can't do that, as RavenDB queries like so:
session.Query<T>();
I don't have the generic type T at compile time, I'm loading that dynamically.
Solution 1 - the Big Document
The first way I tried (for a proof of concept) was to wrap all the entities in a single data object and save that:
public class Data {
List<IEntity> Entities = new List<IEntity>();
}
Assuming the session is opened/closed properly and that I have an id:
var myDataObject = session.Load<Data>(Id);
myDataObject.Entities.First(); // or whatever query
As my types are all dynamic, specified in a dynamically loaded DLL, I need my custom json deserializer to perform the object creation. I specify that in the answer here.
I would rather not do this as loading the entire document in production would not be efficient.
## Possible solution 2 ##
I understand that Lucene can be used to query the type meta data and get the data out as a dynamic type. I can then do a horrible cast and make the changes.
Update after #Tung-Chau
Thank you to Tung, unfortunately neither of the solutions work. Let me explain why:
I am storing the entities with:
session.Store(myDataObject);
In the database, that will produce a document with the name of myDataObject.GetType().Name. If you do:
var myDataObject = session.Load<IEntity>(Id);
Then it won't find the document because it is not saved with the name IEntity, it is saved with the name of the dynamic type.
Now the Lucene solution doesn't work either but for a slightly more complex reason. When Lucene finds the type (which it does), Raven passes it to the custom JsonDeserialiser I mentioned. The custom Json Deserialiser does not have access to the meta data, so it does not know which type to reflect.
Is there a better way to retrieve data when you know the type but not at compile time?
Thank you.
If you have an ID (or IDs):
var myDataObject = session.Load<IEntity>(Id);
//change myDataObject. Use myDataObject.GetType() as you want
//....
session.SaveChange();
For query, LuceneQuery is suitable
var tag = documentStore.Conventions.GetTypeTagName(typeof(YourDataType));
var myDataObjects = session.Advanced
.LuceneQuery<IEntity, RavenDocumentsByEntityName>()
.WhereEquals("Tag", tag)
.AndAlso()
//....
I'm currently developing a small REST API for my application. It works fine. There's one problem, though. The results returned to the user contain too much data. My controller looks like this:
public JsonResult Profile(string name)
{
var encodedName = HttpUtility.HtmlEncode(name);
var n = encodedName.Replace('+', ' ');
var profile = profileSource.GetProfileForName(n);
if (profile == null)
{
HttpContext.Response.StatusCode = 404;
}
// XXXXX: how to remove certain fields from the profile?
return Json(profile, JsonRequestBehavior.AllowGet);
}
Any idea what's the cleanest way to deal with this? Is there some way to annotate the profile model so that certain fields won't get serialized? I guess alternatively I could construct a custom Model for this specific case or hack that JsonResult somehow. Either of these ways adds some extra overhead, though.
You can use the LINQ select method to project into an anonymous type.
You should probably do a custom object with a subset of ProfileĀ“s properties. Then translate the original object to the new DTO like object. (Tip:Try AutoMapper). Serialize and return.
return Json(new{profile.FirstName, profile.LastName, profile.OtherFields}, JsonRequestBehavior.AllowGet);
I ended up using inheritance. Basically I have a "shared" model (base class) and one that derives that and adds some extra fields to it. At the source I simply construct a shared instance for my REST API while the other part of the app uses the derived one. Pretty simple and seems to work well.
I've got a IList of Sites in my application and Site has a large amount of properties.
I'm wanting to convert this list to JSON to be used in a dropdownlist similar to this
var sites = SiteRepository.FindAllSites();
return new JsonResult() { Data = sites, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
the problem I have is that I only want to use the id and name properties of the site class. I was thinking a way round this would be to use an 'adaptor' class that would then only expose these two properties and I would then serialize that.
The problem I have is I want to make the class generic so that it can handle any list of objects. Has anybody come across a similar situation and solved it?
EDIT: I can't use the [ScriptIgnore] Attribute as there may be a case when I do want to serialize the whole class.
Can you create an anonymous class from your list of sites?
var sites = SiteRepository.FindAllSites().Select(x=> new { Id=x.Id, Name=x.Name});
Since FindAllSites() seems to return an IList, which is descended from IEnumberable, you should be able to use System.Linq's extension methods (i.e. Select() ). That'll transform the List<Site> (with all the properties) to IEnumerable<some_anon_class> (with only 2 properties) which is then given to that JsonResult thing instead of the list of Site.
If you decorate you class fields with [ScriptIgnore] (System.Web.Script.Serialization) C# will ignore them during serialization using Json in much the same way that decorating with [XmlIgnore] would for Xml serialization.
MSDN