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

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.

Related

Servicestack orm lite does not deserialize neasted json structures

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
}

Automapper mapping 2 entities to a single class

Just wondering if this is possible:
Say I have a ViewModel for comparing an old and new entity.
public class FooComparison
{
public string Name {get;set;}
public string OldName {get; set;}
public int Age {get; set;}
public int OldAge {get; set;}
...
}
I want to load 2 instances of Foo from the database and populate the FooComparison with the details from both instances.
For now, I have Mapper.CreateMap<Foo, FooComparison>() which will populate the Name, Age etc from the first entity - is there an easy way to populate the Oldxxx properties from the second entity without looping and manually updating them?
My suggestion would be to define a mapping from Tuple<Foo, Foo> to FooComparison:
Mapper.CreateMap<Tuple<Foo, Foo>, FooComparison>()
.ConvertUsing(x => new FooComparison
{
Name = x.Item2.Name,
OldName = x.Item1.Name,
Age = x.Item2.Age,
OldAge = x.Item1.Age,
...
});
Then use it like this:
Foo oldFoo = ...;
Foo newFoo = ...;
FooComparison comparison = Mapper.Map<FooComparison>(Tuple.Create(oldFoo, newFoo));
I appreciate that this loses the "auto" part of automapper, but really the big benefit you are getting by using automapper is that this mapping is defined in just one place in your software, not so much the auto part.
I have personally found this way of mapping via Tuple to work very well.

Removing properties with null values

In my DocumentDb documents, I don't want to include properties with NULL values. For example, I have the following POCO class.
public class Person
{
[JsonProperty(PropertyName="id")]
public int PersonId {get; set;}
[JsonProperty(PropertyName="firstName")]
public string FirstName {get; set;}
[JsonProperty(PropertyName="middleName")]
public string MiddleName {get; set;}
[JsonProperty(PropertyName="lastName")]
public string LastName {get; set;}
}
Some people don't have middle names and when I save a person's document in my collection, I don't want the middle name to be included. Currently, a person without a middle name is saved as:
{
"id": 1234,
"firstName": "John",
"middleName": null,
"lastName": "Smith"
}
Is this normal behavior? If not, how do I NOT include the middle name property with a NULL value in my document?
P.S. All serialization/deserialization is handled by JSON.NET
You can do that when you initialize the Cosmos Client, there's a serialization option which is similar to the JSON.Net.
CosmosClient client = new CosmosClient(yourConnectionString, new CosmosClientOptions()
{
SerializerOptions = new CosmosSerializationOptions()
{
IgnoreNullValues = true,
}
});
I think I found the answer. Looks like I can tell JSON.NET to ignore properties with NULL values using
NullValueHandling = NullValueHandling.Ignore
Here's the documentation:
http://james.newtonking.com/archive/2009/10/23/efficient-json-with-json-net-reducing-serialized-json-size

How to use AutoMapper to map a DataRow to an object in a WCF service?

I have a WCF service that calls a stored procedure and returns a DataTable. I would like to transform the DataRows to custom object before sending to the consumer but can't figure out how to do so. Lets say I retrieved a customer from a stored procedure. To keep things short here is the customer via DataRow:
string lastName = dt.Rows[0]["LastName"].ToString();
string firstName = dt.Rows[0]["FirstName"].ToString();
string age = System.Convert.ToInt32(dt.Rows[0]["Age"].ToString());
I can retrieve the customer easily. Now, I created a Customer object like so:
public Customer
{
public string LastName {get;set;}
public string FirstName {get;set;}
public int Age {get;set;}
}
I loaded AutoMapper from the package manager console. I then put a public static method on my customer like so:
public static Customer Get(DataRow dr)
{
Mapper.CreateMap<DataRow, Customer>();
return Mapper.Map<DataRow, Customer>(dr);
}
When I step through my code, every property in the customer returned from Get() is null. What am I missing here? Do I have to add a custom extension to map from a DataRow? Here is a thread that seems related but I thought AutoMapper would support this out of the box especially since the property names are identical to the column names. Thanks in advance.
This works!!
public static Customer GetSingle(DataTable dt)
{
if (dt.Rows.Count > 0) return null;
List<Customer> c = AutoMapper.Mapper.DynamicMap<IDataReader, List<Customer>>(dt.CreateDataReader());
return c[0];
}

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.

Resources