How to structure in-memory entity classes to load denormalized referenced documents from RavenDB - ravendb

I am receiving a FormatException when trying to load a an Album document from the default RavenDB database:
using (var session = _documentStore.OpenSession())
{
var album = session.Load<Album>(500);
//....
}
The Album JSON document in the database looks like this:
{
"AlbumArtUrl": "/Content/Images/placeholder.gif",
"Genre": {
"Id": "genres/10",
"Name": "Classical"
},
"Price": 8.99,
"Title": "The Best of Beethoven",
"CountSold": 0,
"Artist": {
"Id": "artists/203",
"Name": "Nicolaus Esterhazy Sinfonia"
}
}
And my in-memory entity Album class looks like this:
public class Album
{
public long Id { get; set; }
public string AlbumArtUrl { get; set; }
public DenomralizedGenre Genre { get; set; }
public decimal Price { get; set; }
public string Title { get; set; }
public int CountSold { get; set; }
public DenomralizedArtist Artist { get; set; }
}
public class DenomralizedGenre
{
public int Id { get; set; }
public string Name { get; set; }
}
public class DenomralizedArtist
{
public int Id { get; set; }
public string Name { get; set; }
}
What am I doing wrong here?

Make all your Id strings. You have them as int and long. In RavenDB Id's are strings.
The Id as a string would be Album/24 in RavenDB. The Class name or type plus the HiLo value (created by the client tools) make up the Id.

Related

Unable to parse JSON from Telnyx Api

I am developing an endpoint in C# to accept JSON posted from an external provider (Telnyx). Here is a sample of the data:
{
"data": {
"event_type": "fax.received",
"id": "e15c28d4-147e-420b-a638-2a2647315577",
"occurred_at": "2021-11-19T16:37:02.863682Z",
"payload": {
"call_duration_secs": 35,
"connection_id": "1771912871052051547",
"direction": "inbound",
"fax_id": "2a168c93-3db5-424b-a408-b70a3da625bc",
"from": "+12399999999",
"media_url": "https://s3.amazonaws.com/faxes-prod/999",
"page_count": 1,
"partial_content": false,
"status": "received",
"to": "+12399999999",
"user_id": "dc6e79fa-fe3b-462b-b3a7-5fb7b3111b8a"
},
"record_type": "event"
},
"meta": {
"attempt": 1,
"delivered_to": "https://webhook.site/27ef892c-c371-4976-ae22-22deea57080e"
}
}
I have verified this is valid JSON through https://jsonlint.com/. I created a model:
public class myDeserializedClass
{
public class Payload
{
public int call_duration_secs { get; set; }
public string connection_id { get; set; }
public string direction { get; set; }
public string fax_id { get; set; }
public string from { get; set; }
public string media_url { get; set; }
public int page_count { get; set; }
public bool? partial_content { get; set; }
public string status { get; set; }
public string to { get; set; }
public string user_id { get; set; }
}
public class Data
{
public string event_type { get; set; }
public string id { get; set; }
public DateTime occurred_at { get; set; }
public Payload payload { get; set; }
public string record_type { get; set; }
}
public class Meta
{
public int attempt { get; set; }
public string delivered_to { get; set; }
}
public class Root
{
public Data data { get; set; }
public Meta meta { get; set; }
}
}
The controller being posted to looks like:
[HttpPost]
public IActionResult InboundFax(myDeserializedClass json)
{
try
{
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(json.ToString().Trim());
return Content("OK");
}
catch(Exception ex)
{
return Content(ex.ToString());
}
}
I am receiving the error: Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: K. Path '', line 0, position 0. each time the API tries to post to my endpoint. I have also tried posting data using Postman and receive the same error message. Additionally, there are examples of JSON posting on the API website at https://developers.telnyx.com/docs/v2/programmable-fax/tutorials/receive-a-fax-via-api. Since my application fails with both postman and real-time API calls, I'm am working on the assumption the problem is my code, but can't be 100% certain and don't know how to fix it. This is a mission critical problem that I need to solve. Any help would be appreciated.
First of all, the class is bad. Should be:
public class Payload
{
public int call_duration_secs { get; set; }
public string connection_id { get; set; }
public string direction { get; set; }
public string fax_id { get; set; }
public string from { get; set; }
public string media_url { get; set; }
public int page_count { get; set; }
public bool? partial_content { get; set; }
public string status { get; set; }
public string to { get; set; }
public string user_id { get; set; }
}
public class Data
{
public string event_type { get; set; }
public string id { get; set; }
public DateTime occurred_at { get; set; }
public Payload payload { get; set; }
public string record_type { get; set; }
}
public class Meta
{
public int attempt { get; set; }
public string delivered_to { get; set; }
}
public class myDeserializedClass
{
public Data data { get; set; }
public Meta meta { get; set; }
}
It depends on the data that you are getting, but if you are getting the object, you don't need to convert it to work:
[HttpPost]
public IActionResult InboundFax(myDeserializedClass json)
{
try
{
//Work directly with json as object, forget "root" is: myDeserializedClass
return Content("OK");
}
catch(Exception ex)
{
return Content(ex.ToString());
}
}
or if you are getting the json as string:
[HttpPost]
public IActionResult InboundFax(string json)
{
try
{
//Work directly with json as object
myDeserializedClass myInstance= JsonConvert.DeserializeObject<myDeserializedClass>(json);
return Content("OK");
}
catch(Exception ex)
{
return Content(ex.ToString());
}
}
UPDATE AFTER TESTING IT:
I test it with a dummy controller:
[HttpPost]
public ActionResult InboundFax(myDeserializedClass json)
{
try
{
//Just dummy test
if (json.meta.attempt == 1)
{
return Content("OK");
}
else {
return Content("NO");
}
//Work directly with json as object, forget "root" is: myDeserializedClass
}
catch (Exception ex)
{
return Content(ex.ToString());
}
}
in a HomeController (blank template from MVC Web just dummy)
So posting to:
https://localhost:44334/Home/InboundFax
METHOD: POST
With the following data:
{
"data": {
"event_type": "fax.received",
"id": "e15c28d4-147e-420b-a638-2a2647315577",
"occurred_at": "2021-11-19T16:37:02.863682Z",
"payload": {
"call_duration_secs": 35,
"connection_id": "1771912871052051547",
"direction": "inbound",
"fax_id": "2a168c93-3db5-424b-a408-b70a3da625bc",
"from": "+12399999999",
"media_url": "https://s3.amazonaws.com/faxes-prod/999",
"page_count": 1,
"partial_content": false,
"status": "received",
"to": "+12399999999",
"user_id": "dc6e79fa-fe3b-462b-b3a7-5fb7b3111b8a"
},
"record_type": "event"
},
"meta": {
"attempt": 1,
"delivered_to": "https://webhook.site/27ef892c-c371-4976-ae22-22deea57080e"
}
}
Little quickwatch you see it map everything:
Could mean the POSTMAN is wrong configurated?
I use the following header:
Content-Type: application/json
I'm using TALEND API TESTER for Chrome, but every REST client is similar
With POSTMAN, same result, OK. Check for body: raw, type JSON, and header with the content type applicantion/json
Well, I am not sure if I have an answer or not, however, I did manage to get the application working by changing the endpoint to a WebApi instead of a MVC controller. I was under the impression a MVC controller could accept json data, however, I was unable to ever get it working. Once I changed it, everyting worked perfectly.

Empty list in Ravendb index creating exception

I have a problem that an index I've made when queried is throwing an exception :
Could not read value for property: Members
Here is the index :
public class GroupsNameIdIndex : AbstractIndexCreationTask<CommunityDocument, GroupsNameIdIndex.Result>
{
public class Result
{
public string CommunityId { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public IList<string> Members { get; set; }
}
public GroupsNameIdIndex()
{
Map = communities => from community in communities
from newGroup in community.Groups
select new
{
CommunityId = community.Id.Replace("CommunityDocuments/", string.Empty),
newGroup.Id,
newGroup.Name,
newGroup.Members
};
StoreAllFields(FieldStorage.Yes);
}
}
This is the query :
var groupResult = session
.Query<GroupsNameIdIndex.Result, GroupsNameIdIndex>()
.Where(x => x.CommunityId == info.Id)
.AsProjection<GroupsNameIdIndex.Result>()
.ToList();
I only have 1 group in the document with relevant Id and the Members node is an empty list, not null. When I manually populate it with a single string, then the query runs fine. Why are empty lists not allowed? If this is a restriction, it makes indexing pointless for me, because I will have a lot of empty lists that are just going to make the application fail everywhere.
edit: Adding the class in case that has something to do with it:
public class CommunityGroup
{
public CommunityGroup()
{
Members = new List<string>();
MemberDetails = new List<MemberView>();
MemberNames = new List<string>();
}
public string Id { get; set; }
public string Name { get; set; }
public string Slug { get; set; }
public string Description { get; set; }
public bool AdminOnly { get; set; }
public bool NewsContribute { get; set; }
public bool AutoGroup { get; set; }
public int LowerAgeLimit { get; set; }
public int UpperAgeLimit { get; set; }
public string Gender { get; set; }
public IList<string> Members { get; set; }
public IList<string> MemberNames { get; set; }
public IList<MemberView> MemberDetails { get; set; }
public string RelatedCommunityId { get; set; }
public string CreatedBy { get; set; }
public bool SmartGroup { get; set; }
public IList<SmartGroupRules> AndRules { get; set; }
public IList<SmartGroupRules> OrRules { get; set; }
public string ParentGroup { get; set; }
public List<string> EntryGroups { get; set; }
public bool Hidden { get; set; }
public string ActivityType { get; set; }
public List<string> AnyGroupList { get; set; }
public List<string> AllGroupList { get; set; }
public GroupType GroupType { get; set; }
}
and here is the json of the group in the db
"Groups": [
{
"Id": "5ja34tefoq7sfj",
"Name": "new test",
"Slug": "new-test",
"Description": null,
"AdminOnly": true,
"NewsContribute": false,
"AutoGroup": false,
"LowerAgeLimit": 0,
"UpperAgeLimit": 0,
"Gender": null,
"Members": [],
"MemberNames": [],
"MemberDetails": [],
"RelatedCommunityId": null,
"CreatedBy": "Activity",
"SmartGroup": false,
"AndRules": null,
"OrRules": null,
"ParentGroup": null,
"EntryGroups": null,
"Hidden": false,
"ActivityType": null,
"AnyGroupList": null,
"AllGroupList": null,
"GroupType": "Default"
}
],

response deserialize using json.net returns null

I have the following json:
{
"13377": {
"id": 13377,
"orderId": 13377,
"executionStatus": "-1",
"comment": "",
"htmlComment": "",
"cycleId": -1,
"cycleName": "Ad hoc",
"versionId": 10001,
"versionName": "Version2",
"projectId": 10000,
"createdBy": "vm_admin",
"modifiedBy": "vm_admin",
"assignedTo": "user1",
"assignedToDisplay": "user1",
"assignedToUserName": "user1",
"assigneeType": "assignee",
"issueId": 10013,
"issueKey": "SAM-14",
"summary": "Test",
"label": "",
"component": "",
"projectKey": "SAM",
"folderId": 233,
"folderName": "testfolder"
}
}
I create the following classes using json2csharp and get:
public class __invalid_type__13377
{
public int id { get; set; }
public int orderId { get; set; }
public string executionStatus { get; set; }
public string comment { get; set; }
public string htmlComment { get; set; }
public int cycleId { get; set; }
public string cycleName { get; set; }
public int versionId { get; set; }
public string versionName { get; set; }
public int projectId { get; set; }
public string createdBy { get; set; }
public string modifiedBy { get; set; }
public string assignedTo { get; set; }
public string assignedToDisplay { get; set; }
public string assignedToUserName { get; set; }
public string assigneeType { get; set; }
public int issueId { get; set; }
public string issueKey { get; set; }
public string summary { get; set; }
public string label { get; set; }
public string component { get; set; }
public string projectKey { get; set; }
public int folderId { get; set; }
public string folderName { get; set; }
}
public class RootObject
{
public __invalid_type__13377 __invalid_name__13377 { get; set; }
}
When I deserialize in C# I receive no error but I get null?
Not sure how to approach this..any suggestions would be welcome.
thankyou.
Your Json object can be deserialized with an Dictionary. On this case, you can build your DataObject like you are doing:
public class Foo
{
public int id { get; set; }
public int orderId { get; set; }
...
}
And Deserialize to
Dictionary<string, Foo>
If you are using Newtonsoft:
var data = JsonConvert.DeserializeObject<Dictionary<string,Foo>(json);
You can use sometimes, when the type of object is unknown, the dynamic keyword:
var data = JsonConvert.DeserializeObject<dynamic>(json);

Portable Library Error on deserialization array of objects

I have a WebApi returning the following JSON which I am trying to deserialize to the object below
JSON OBJECT
{
"results": [{
"id": 123456,
"fullName": "Foo Bar",
"localName": null,
"jobPosition": "ME",
"jobCompanyName": "EXTRA",
"jobLocationCountry": "United States of America",
"jobLocationCity": "San Francisco",
"jobCountrySubdivision": "California",
"boards": [],
"restrictionsIndicator": false,
"personRestriction": null,
"jobRestriction": null
}, {
"id": 789101,
"fullName": "Foo Bar",
"localName": null,
"jobPosition": null,
"jobCompanyName": "Unknown",
"jobLocationCountry": "Unknown",
"jobLocationCity": "Unknown",
"jobCountrySubdivision": "Unknown",
"boards": [{
"companyId": 667525,
"companyName": "FOO BAR COMPANY",
"companyOffLimits": null,
"restrictionCategoryId": null
}
],
"restrictionsIndicator": false,
"personRestriction": null,
"jobRestriction": null
}
],
"totalCount": 2,
"pageNumber": 1,
"resultsPerPage": 100
}
C# Classes
public class Rootobject
{
public Result[] results { get; set; }
public int totalCount { get; set; }
public int pageNumber { get; set; }
public int resultsPerPage { get; set; }
}
public class Result
{
public int id { get; set; }
public string fullName { get; set; }
public object localName { get; set; }
public string jobPosition { get; set; }
public string jobCompanyName { get; set; }
public string jobLocationCountry { get; set; }
public string jobLocationCity { get; set; }
public string jobCountrySubdivision { get; set; }
public Board[] boards { get; set; }
public bool restrictionsIndicator { get; set; }
public int? personRestriction { get; set; }
public int? jobRestriction { get; set; }
}
public class Board
{
public int companyId { get; set; }
public string companyName { get; set; }
public int? companyOffLimits { get; set; }
public object restrictionCategoryId { get; set; }
}
The DLL is a Portable Class Library which is .NET 4.5 and i have JSON.net(10.0.1) installed via nuget, but the portable library is connected to a xamarin IOS Project on a mac.
If the JSON being deserialized has no Boards it works out fine but if there is a Board then I receive the following message.
Unable to find a constructor to use for type Board. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'results[1].boards[0].companyId'
The Settings I am using are:
var settings = new Newtonsoft.Json.JsonSerializerSettings
{
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(),
};
I have tried the following ways to get it to serialize:
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<Rootobject>(_jsonString, settings);
var jobject = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType(_jsonString, new Rootobject());
i have tried the following
Put in a default constructor
Naming all the parameters for the class in a constructor
Adding the Attribute to the constructor
Changing the Boards to a List
Taking out the Boards Property
but there is still no joy. It will not deserialize for me.
I think you have to modify this
public class Board
{
public int companyId { get; set; }
public string companyName { get; set; }
public int? companyOffLimits { get; set; }
public object restrictionCategoryId { get; set; }
**public Board(){}**
}
also in other classes
or also change
public Board[] boards { get; set; }
to
public List<Board> boards { get; set; }
Try....

RavenDB: How to serialize a class differently when embedded versus as a document

The sample db that comes with RavenDB has Albums document collection, that each have a Genre embedded document in it, like this:
{
... other stuff...
"Genre": {
"Id": "genres/1",
"Name": "Rock"
},
... other stuff...
}
Notice that here the Genre has Id and Name fields.
But when you look at Genre documents, they have Id, Name, and Description fields, like this:
{
"Description": "Rock and Roll is a form of rock music developed in the 1950s and 1960s. Rock music combines many kinds of music from the United States, such as country music, folk music, church music, work songs, blues and jazz.",
"Name": "Rock"
}
How can I model in code so that when I Store() then SaveChanges() that the Genre will be serialized and saved differently (like the example data) when saving its own document versus when saving the Album document and embedding the Genre?
This model will match the ravendb sample data:
public class Genre
{
public string Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
}
public class Album
{
public string Id { get; set; }
public string AlbumArtUrl { get; set; }
public GenreRef Genre { get; set; }
public decimal Price { get; set; }
public string Title { get; set; }
public int CountSold { get; set; }
public ArtistRef Artist { get; set; }
public class GenreRef
{
public string Id { get; set; }
public string Name { get; set; }
}
public class ArtistRef
{
public string Id { get; set; }
public string Name { get; set; }
}
}
Example usage with includes:
var albumId = "albums/1";
var album = session.Include<Album>(x => x.Genre.Id)
.Load<Album>(albumId);
var genre = session.Load<Genre>(album.Genre.Id);
The include means that when loading the Genre, there is no db call because it's already tracked in the session.