Servicestack orm lite does not deserialize neasted json structures - servicestack

I have a pgsql view which returns list of records. One field of record is represented as json and deserialised to property List<ClassA> ClassAItems. However ClassAItems has also List<ClassB> ClassBItems and deserialisation doesnt not work at this level.
How to use Autquery or OrmLite is such case to process the query response so the items from ClassAItems will contain properly deserialised ClassBItems.
[Alias("vw_someview_with_json_field")]
public class ViewItem
{
public Id {get;set;} // it is properly deserialised
public List<ClassA> ClassAItems {get;set;} // hierarchical json data
}
public class ClassA {
public int Id {get;set;} // it is deserialised correctly
public List<ClassB> ClassBItems {get;set;} // <--- here is the issue, it is not deserialised
}

Related

Change Autoquery return type in DTO generation

I want to return a custom class from my custom AutoQuery endpoint that inherits QueryResponse<T> but adds a few extra properties.
public class WritingAssignmentBlogLookUpResponse : QueryResponse<BlogDto>, IResponse
{
public bool Success { get; set; }
public string Message { get; set; }
public string DebugMessage { get; set; }
}
But if I specify request like so:
[Route("/assignment/blogs/", "POST")]
public class WritingAssignmentBlogsLookUpRequest : QueryDb<Blog, BlogDto>, IReturn<WritingAssignmentBlogLookUpResponse>
{
}
Then the return type specified in generatd DTO for client.post(req) is QueryResponse<BlogDto> and it doesn't generate WritingAssignmentBlogLookUpResponse at all.
Do I just have to specify return type as any from my typescript service or is there a way to make the types match so I can strongly type it?
You can’t change AutoQuery responses which are already fixed in their service contract definition to return a QueryResponse<T>.
You can add extra info to the Meta Dictionary of the Response DTO (exists for this reason) otherwise if you need to change the Service Contract you’d need to convert it into a normal (I.e. non-AutoQuery) API which could use the Service Gateway to call an existing AutoQuery API that decorates the response.

how to remove one property of an object when it is null while adding in table storage?

We have below entity inherited from tableEntity
public class LinkEntity : TableEntity
{
public string LinkKey {get; set;}
public string LinkName {get; set;}
public int LinkValue {get; set;}
public string LinkId {get {return PartitionKey;} set;}
public LinkEntity(Link link)
{
PartitionKey = link.LinkId;
RowKey = link.LinkKey;
LinkValue = link.Value;
LinkName = link.LinkName;
}
}
I have an API that adds the above entity using Post and below steps:
linkValue of Link is null.
var cloudTable = cloudTableClient.GetTableReference(LinkTable);
cloudTable.CreateIfNotExistsAsync();
var postOperation = TableOperation.Insert(LinkEntity(link));
cloudTable.ExecuteAsync(postOperation);
But, when I do get on above, I again receive linkValue as null.
Hence, I don't want to add this value in tableStorage or column/property for entity when this value is null.
I cannot get rid of property linkValue completely because it is being used by other API which is a required field over there. Hence, any advise would be appreciated.
I think there should be some way where we can add required fields and ignore or remove columns completely from entities since tables in table storage is schemaless.
TL;DR;
Please make the LinkValue property nullable. That should solve the problem. So your entity definition would be:
public class LinkEntity : TableEntity
{
public string LinkKey {get; set;}
public string LinkName {get; set;}
public int? LinkValue {get; set;}
public string LinkId {get {return PartitionKey;} set;}
public LinkEntity(Link link)
{
PartitionKey = link.LinkId;
RowKey = link.LinkKey;
LinkValue = link.Value;
LinkName = link.LinkName;
}
}
Longer Version (Somewhat) :)
As you rightly mentioned, Azure Tables are schema less. Another important thing to understand is that there's no concept of null values in an entity in Azure Tables. Either an attribute is present in an entity or it is not.
By keeping int as the data type (which has a default value of 0) for your LinkValue attribute, even if you don't provide any value, this attribute will be initialized with default value and that gets stored.
By making the data type as nullable int, if you don't provide any value for this attribute, it won't get initialized and will be ignored by the SDK when the entity gets serialized.
However you will need to ensure that the application which consumes this entity (i.e. the receiving end) does not assume that the value will always be present in this attribute and should be prepared to handle null values.

Orchard: how to persist a record without content

Allright, this should be fairly easy.
I would like to persist some records for my module in Orchard (1.7.2) without those records being also a ContentPartRecord.
In other words, I would like to be able to persist in DB the following objects:
public class LogItemRecord
{
public virtual string Message { get; set; }
}
..which is already mapped on to the db. But notice that this class is not derived from ContentPartRecord, as it is most certainly not one.
However, when I call IRepository instance's .Create method, all I get is a lousy nHibernate exception:
No persister for: MyModule.Models.LogItemRecord
...which disappears if I do declare the LogItem record as having been inherited from ContentPartRecord, but trying to persist that, apart from being hacky-tacky, runs into an exception of its own, where nHibernate again justly complains that the Id value for the record is zero, though in not so many words.
So... how do I play nicely with Orchard and use its API to persist objects of my own that are not ContentParts / ContentItems?
I'm running 1.7.3 (also tested in 1.7.2) and have successfully been able to persist the following class to the DB:
public class ContactRecord
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string JobTitle { get; set; }
public virtual string Email { get; set; }
public virtual string Phone { get; set; }
}
Here are the relevant lines from Migrations.cs
SchemaBuilder.CreateTable(
typeof(ContactRecord).Name,
table => table
.Column<int>("Id", col => col.Identity().PrimaryKey())
.Column<string>("Name")
.Column<string>("JobTitle")
.Column<string>("Email")
.Column<string>("Phone")
);
I'm going to assume that the code you've shown for LogItemRecord is the complete class definition when making the following statement...
I think that any Record class you store in the DB needs an Id property, and that property should be marked as Identity and PrimaryKey in the table definition (as I've done above).
When you create a *Record class which inherits from ContentPartRecord and setup the table like
SchemaBuilder.CreateTable(
"YourRecord",
table => table
.ContentPartRecord()
// more column definitions
);
then you get the Id property/PK "for free" by inheritance and calling .ContentPartRecord() in the Migration.
See the PersonRecord in the Orchard Training Demo Module for another example of storing a standard class as a record in the DB.

How update an entity inside Aggregate

I have an aggregate named Campaigns every with a root entity named campaign, this root entity has a list of attempts (entity)
public class Attempts: IEntity<Attempts>
{
private int id;
public AttempNumber AttemptNumber {get;}
//other fields
}
public class Campaign: IEntity<Campaign> //root
{
private int id;
public IList<Attempt> {get;}
//other fields
}
Im using a method to add a campaign attempt
public virtual void AssignAttempts(Attempts att)
{
Validate.NotNull(att, "attemps are required for assignment");
this.attempts.add(att);
}
Problem comes when i try to edit a specific item in attempts list. I get Attempt by AttempNumber and pass it to editAttempt method but i dont know how to set the attempt without deleting whole list and recreate it again
public virtual void EditAttempts(Attempts att)
{
Validate.NotNull(att, "attemps are required for assignment");
}
Any help will be appreciated!
Thanks,
Pedro de la Cruz
First, I think there may be a slight problem with your domain model. It seems to me like 'Campaign' should be an aggregate root entity having a collection of 'Attempt' value objects (or entities). There is no 'Campaigns' aggregate unless you have a parent concept to a campaign which would contain a collection of campaigns. Also, there is no 'Attempts' entity. Instead a collection of 'Attempt' entities or values on the 'Campaign' entity. 'Attempt' may be an entity if it has identity outside of a 'Campaign', otherwise it is a value object. The code could be something like this:
class Campaign {
public string Id { get; set; }
public ICollection<Attempt> Attempts { get; private set; }
public Attempt GetAttempt(string id) {
return this.Attempts.FirstOrDefault(x => x.Number == id);
}
}
class Attempt {
public string Number { get; set; }
public string Attribute1 { get; set; }
}
If you retrieve an Attempt from the Campaign entity and then change some of the properties, you should not have to insert it back into the campaign entity, it is already there. This is how the code would look if you were using NHibernate (similar for other ORMs):
var campaign = this.Session.Get<Campaign>("some-id");
var attempt = campaign.GetAttempt("some-attempt-id");
attempt.Attribute1 = "some new value";
this.Session.Flush(); // will commit changes made to Attempt
You don't need an Edit method. Your code can modify the Attempts in-place, like so:
Attempt toModify = MyRepository.GetAttemptById(id);
toModify.Counter++;
toModify.Location = "Paris";
MyRepository.SaveChanges(); // to actually persist to the DB
Of course how you name the SaveChanges() is up to you, this is the way Entity Framework names its general Save method.

'Unexpected element: XX' during deserialization MongoDB C#

I'm trying to persist an object into a MongoDB, using the following bit of code:
public class myClass
{
public string Heading { get; set; }
public string Body { get; set; }
}
static void Main(string[] args)
{
var mongo = MongoServer.Create();
var db = mongo.GetDatabase("myDb");
var col = db.GetCollection<BsonDocument>("myCollection");
var myinstance = new myClass();
col.Insert(myinstance);
var query = Query.And(Query.EQ("_id", new ObjectId("4df06c23f0e7e51f087611f7)));
var res = col.Find(query);
foreach (var doc in res)
{
var obj = BsonSerializer.Deserialize<myClass>(doc);
}
}
However I get the following exception 'Unexpected element: _id' when trying to Deserialize the document.
So do I need to Deserialize in another way?? What is the preferred way of doing this?
TIA
Søren
You are searching for a given document using an ObjectId but when you save an instance of MyClass you aren't providing an Id property so the driver will create one for you (you can make any property the id by adding the [BsonId] attribute to it), when you retrieve that document you don't have an Id so you get the deserialization error.
You can add the BsonIgnorExtraElements attribute to the class as Chris said, but you should really add an Id property of type ObjectId to your class, you obviously need the Id (as you are using it in your query). As the _id property is reserved for the primary key, you are only ever going to retrieve a single document so you would be better off writing your query like this:
col.FindOneById(new ObjectId("4df06c23f0e7e51f087611f7"));
The fact that you are deserializing to an instance of MyClass once you retrieve the document lends itself to strongly typing the collection, so where you create an instance of the collection you can do this
var col = db.GetCollection<MyClass>("myCollection");
so that when you retrieve the document using the FindOneById method the driver will take care of the deserialization for you putting it all together (provided you add the Id property to the class) you could write
var col = db.GetCollection<MyClass>("myCollection");
MyClass myClass = col.FindOneById(new ObjectId("4df06c23f0e7e51f087611f7"));
One final thing to note, as the _id property is created for you on save by the driver, if you were to leave it off your MyClass instance, every time you saved that document you would get a new Id and hence a new document, so if you saved it n times you would have n documents, which probably isn't what you want.
A slight variation of Projapati's answer. First Mongo will deserialize the id value happily to a property named Id which is more chsarp-ish. But you don't necessarily need to do this if you are just retrieving data.
You can add [BsonIgnoreExtraElements] to your class and it should work. This will allow you to return a subset of the data, great for queries and view-models.
Try adding _id to your class.
This usually happens when your class doesn't have members for all fields in your document.
public class myClass
{
public ObjectId _id { get; set; }
public string Heading { get; set; }
public string Body { get; set; }
}

Resources