The scenario is the following: I receive a message containing a lot of variables, several hundreds. I need to write this to Azure Table storage where the partition key is the name of the individual variables and the value gets mapped to e.g. Value.
Let’s say the payload looks like the following:
public class Payload
{
public long DeviceId { get; set; }
public string Name { get; set; }
public double Foo { get; set; }
public double Rpm { get; set; }
public double Temp { get; set; }
public string Status { get; set; }
public DateTime Timestamp { get; set; }
}
And my TableEntry like this:
public class Table : TableEntity
{
public Table(string partitionKey, string rowKey)
{
this.PartitionKey = partitionKey;
this.RowKey = rowKey;
}
public Table() {}
public long DeviceId { get; set; }
public string Name { get; set; }
public double Value { get; set; }
public string Signal { get; set; }
public string Status { get; set; }
}
In order to write that to Table storage, I need to
var table = new Table(primaryKey, payload.Timestamp.ToString(TimestampFormat))
{
DeviceId = payload.DeviceId,
Name = payload.Name,
Status = payload.Status,
Value = value (payload.Foo or payload.Rpm or payload.Temp),
Signal = primarykey/Name of variable ("foo" or "rmp" or "temp"),
Timestamp = payload.Timestamp
};
var insertOperation = TableOperation.Insert(table);
await this.cloudTable.ExecuteAsync(insertOperation);
I don’t want to copy this 900 times (or how many variables there happen to be in the payload message; this is a fixed number).
I could make a method to create the table, but I will still have to call this 900 times.
I thought maybe AutoMapper could help out.
Are they always the same variables? A different approach could be to use DynamicTableEntity in which you basically have a TableEntity where you can fill out all additional fields after the RowKey/PartitionKey Duo:
var tableEntity = new DynamicTableEntity();
tableEntity.PartitionKey = "partitionkey";
tableEntity.RowKey = "rowkey";
dynamic json = JsonConvert.DeserializeObject("{bunch:'of',stuff:'here'}");
foreach(var item in json)
{
tableEntity.Properties.Add(item.displayName, item.value);
}
// Save etc
The problem is to map these properties, it is right?
Value = value (payload.Foo or payload.Rpm or payload.Temp),
Signal = primarykey/Name of variable ("foo" or "rmp" or "temp"),
This conditional mapping can be done via Reflection:
object payload = new A { Id = 1 };
object value = TryGetPropertyValue(payload, "Id", "Name"); //returns 1
payload = new B { Name = "foo" };
value = TryGetPropertyValue(payload, "Id", "Name"); //returns "foo"
.
public object TryGetPropertyValue(object obj, params string[] propertyNames)
{
foreach (var name in propertyNames)
{
PropertyInfo propertyInfo = obj.GetType().GetProperty(name);
if (propertyInfo != null) return propertyInfo.GetValue(obj);
}
throw new ArgumentException();
}
You may map rest of properties (which have equal names in source and destination) with AutoMapper.Mapper.DynamicMap call instead of AutoMapper.Mapper.Map to avoid creation of hundreds configuration maps. Or just cast your payload to dynamic and map it manually.
You can create a DynamicTableEntity from your Payload objects with 1-2 lines of code using TableEntity.Flatten method in the SDK or use the ObjectFlattenerRecomposer Nuget package if you are also worried about ICollection type properties. Assign it PK/RK and write the flattened Payload object into the table as a DynamicTableEntity. When you read it back, read it as DynamicTableEntity and you can use TableEntity.ConvertBack method to recreate the original object. Dont even need that intermediate Table class.
Related
I have an application that has two similar but different objects and I want to store those objects in the same collection. What is the best way to do this? And how can I query this collection?
Today my collections is represented by:
public IMongoCollection<Post> Posts
{
get
{
return _database.GetCollection<Post>("posts");
}
}
And I have this class:
public class Post
{
public string Id { get; set; }
public string Message { get; set; }
}
public class NewTypePost
{
public string Id { get; set; }
public string Image { get; set; }
}
So, today I just can save and query using Post class. Now I want to store and retrive the both classes, Post and NewTypePost.
I tried to change the class type from Post to dynamic. But when I did this, I could not query the collections.
MongoDB .NET driver offers few possibilites in such cases:
Polymorphism
You can build a hierarchy of classes and MongoDB driver will be able to determine a type of an object it gets retrieved from the database:
[BsonKnownTypes(typeof(Post), typeof(NewTypePost))]
public abstract class PostBase
{
[BsonId]
public string Id { get; set; }
}
public class Post: PostBase
{
public string Message { get; set; }
}
public class NewTypePost: PostBase
{
public string Image { get; set; }
}
MongoDB driver will create additional field _t in every document which will represent corresponding class.
Single Class
You can still have Post class and use BsonIgnoreIfNull attribute to avoid serialization exception. MongoDB .NET driver will set those properties to null if they don't exist in your database.
public class Post
{
[BsonId]
public string Id { get; set; }
[BsonIgnoreIfNull]
public string Message { get; set; }
[BsonIgnoreIfNull]
public string Image { get; set; }
}
BsonDocument
You can also drop strongly-typed approach and use BsonDocument class which is dynamic dictionary-like structure that represents your Mongo documents
var collection = db.GetCollection<BsonDocument>("posts");
More details here
dynamic
Specifying dynamic as generic parameter of ICollection you should get a list of ExpandoObject that will hold all the values you have in your database.
var collection = db.GetCollection<dynamic>("posts");
var data = collection.Find(Builders<dynamic>.Filter.Empty).ToList();
var firstMessage = data[0].Message; // dynamically typed code
Suppose I have the next conn to a test database:
var mongoClient = new MongoClient(new MongoClientSettings
{
Server = new MongoServerAddress("localhost"),
});
var database = mongoClient.GetDatabase("TestDb");
Then I can do something like:
var col = database.GetCollection<Post>("posts");
var col2 = database.GetCollection<NewTypePost>("posts");
To get two different instances of IMongoCollection but pointing to the same collection in the database. Further I am able to save to each collection in the usual way:
col.InsertOne(new Post { Message = "m1" });
col2.InsertOne(new NewTypePost { Image = "im1" });
Then, I'm also able to query from those collection base on the specific fields:
var p1= col.Find(Builders<Post>.Filter.Eq(x=>x.Message, "m1")).FirstOrDefault();
var p2 =col2.Find(Builders<NewTypePost>.Filter.Eq(x=>x.Image, "im1")).FirstOrDefault();
Console.WriteLine(p1?.Message); // m1
Console.WriteLine(p2?.Image); // im1
I don't know if that's what you want but it uses the same collection. BTW, change the Id properties to be decorated with [BsonId, BsonRepresentation(BsonType.ObjectId)]. Hope it helps.
Use the BsonDocument data type. It can do all of that. BsonDocument and dynamic back and forth is very convenient.
public class CustomObject{
public long Id{get;set;}
public string Name{get;set;}
public List<(string,object)> CollectionDynamic{get;set;}
}
// inserted in mongo
//public class CustomObject_in_Db{
// public long Id {get;set;}
// public string Name {get;set;}
// public string field2 {get;set;}
// public string field3 {get;set;}
// public string field4 {get;set;}
// public string field5 {get;set;}
// }
// something code... mapper(config)
Automapper.Mapper.CreateMap<BsonDocument,CustomObject>()
.ForMember(dest=>dest.Id, a=>a.MapFrom(s=>s.Id.GetValue(nameof(CustomObject.Id)).AsInt64)
.ForMember(dest=>dest.Name, a=>a.MapFrom(s=>s.Id.GetValue(nameof(CustomObject.Name)).AsString)
.ForMember(dest=>dest.CollectionDynamic, a=>a.MapFrom(s=>_getList(s));
// .......
private List<(string, object)> _getList(BsonDocument source){
return source.Elements.Where(e=>!typeof(CustomObject).GetProperties().Select(s=>s.Name).Any(a=>a ==e.Name)).Select(e=>e.Name, BsonTryMapper.MapToDotNetValue(e.Value)));
}
Is there a way to pass child or multiple objects to Dapper as query parameter, when they have properties with the same name?
For example, if I have these classes:
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public City City { get; set; }
}
class City
{
public int Id { get; set; }
public string Name { get; set; }
}
and I want to execute this query:
connect.QueryFirst<Result>("select :Id Id, :Name Name, :City_Id City_Id, :City_Name City_Name from dual", personParams);
The only way I managed to do it without reflection was passing the first object and then adding the other properties one by one:
var personParams = new DynamicParameters(person);
personParams.AddDynamicParams(new { City_Id = person.City.Id, City_name = person.City.Name });
But on the database there are hundreds of tables, some of them with more than a hundred columns, and I need to split them into multiple classes. So passing the parameters one by one would be improductive.
I tried adding the parameters to a temporary bag and then adding a prefix to each one, something like this:
static DynamicParameters GetParameters(object mainEntity, object otherEntities)
{
var allParams = new DynamicParameters(mainEntity);
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(otherEntities))
{
object obj = descriptor.GetValue(otherEntities);
var parameters = new DynamicParameters(obj);
foreach (var paramName in parameters.ParameterNames)
{
var value = parameters.Get<object>(paramName);
allParams.Add($"{descriptor.Name}{paramName}", value);
}
}
return allParams;
}
But it doesn't work because Dapper only populates the parameters when the command is executing, not when the DynamicParamters is created.
I wanted to avoid reflection because Dapper is very good at it, and my code would probably perform much worse. Is there a way to do it or is reflection my only option?
I am working on WCF and I want to get record list array date wise and I need array key as the date which has common in record like below:
{
"EventAppGetAllSessionByCustomerIdResult":{
"02/22/2017":[
{
"SessionDate":"02/22/2017"
}
],
"08/27/2016":[
{
"SessionDate":"08/27/2016"
}
],
"Status":{
"Description":"Successfull!",
"Status":1
}
}
}
Basically, I want to extract values of SessionDate.
I assumed that you want to extract "SessionDate" property from your JSON. I recommend using JObject.Parse() method.
JObject jObject = JObject.Parse(json);
var result = (JObject)jObject["EventAppGetAllSessionByCustomerIdResult"];
var dates = new List<string>();
foreach(JProperty prop in result.Properties())
{
if (prop.Name != "Status")
{
var values = jObject["EventAppGetAllSessionByCustomerIdResult"][prop.Name].Values<string>("SessionDate");
dates.AddRange(values);
}
}
Little explanation:
In your case "02/22/2017" is property which has an array of objects. Each object has "SessionDate" property which holds value. So, following line will extract values from "SessionDate" of all objects:
var values = jObject["EventAppGetAllSessionByCustomerIdResult"][prop.Name].Values<string>("SessionDate");
values represents all dates from a single property. In your case, it can be from "02/22/2017" or from "08/27/2016".
dates will be list of "SessionDate" values. Of course, you have to handle possible exceptions by yourself.
I'm not sure its what you want but try this as your output object:
public class Session
{
public string SessionDate { get; set; }
}
public class Status
{
public string Description { get; set; }
public int Code { get; set; }
}
public class EventAppGetAllSessionByCustomerIdResult
{
public KeyValuePair<string, Session[]>[] EventAppGetAllSessionByCustomerId { get; set; }
public Status Status { get; set; }
}
I have a windows form application and would like to deserialize a JSON string that I'm getting from a web address so that I can get just two values from it, how would I go about doing this?
Below is the code I have to get the JSON string, and if you go to the URL that it's getting, you can also see the JSON string. I want to just get the item name, and current price of it. Which you can see the price under the current key.
private void GrabPrices()
{
using (WebClient webClient = new System.Net.WebClient())
{
WebClient n = new WebClient();
var json = n.DownloadString("http://services.runescape.com/m=itemdb_rs/api/catalogue/detail.json?item=1513");
string valueOriginal = Convert.ToString(json);
Console.WriteLine(json);
}
}
It's also going to be iterating through a SQLite database and getting the same data for multiple items based on the item ID, which I'll be able to do myself.
EDIT I'd like to use JSON.Net if possible, I've been trying to use it and it seems easy enough, but I'm still having trouble.
Okay so first of all you need to know your JSON structure, sample:
[{
name: "Micheal",
age: 20
},
{
name: "Bob",
age: 24
}]
With this information you can derive a C# object
public class Person
{
public string Name {get;set;}
public int Age {get;set;}
}
Now you can use JSON.NET to deserialize your JSON into C#:
var people = JsonConvert.DeserializeObject<List<Person>>(jsonString);
If you look at the original JSON it is an array of objects, to deal with this I have used List<T>.
Key things to remember, you need to have the C# object mirror in properties that of the JSON object. If you don't have a list, then you don't need List<T>.
If your JSON objects have camel casing, and you want this converted to the C# conventions, then use this:
var people = JsonConvert.DeserializeObject<List<Person>>(
jsonString,
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
First of all you need to create a class structure for the JSON
public class Wrapper
{
public Item item;
}
public class Item
{
public string icon { get; set; }
public string icon_large { get; set; }
public int id { get; set; }
public string type { get; set; }
public string typeIcon { get; set; }
public string name { get; set; }
public string description { get; set; }
public GrandExchange current { get; set; }
public GrandExchange today { get; set; }
public bool members { get; set; }
public GrandExchange day30 { get; set; }
public GrandExchange day90 { get; set; }
public GrandExchange day180 { get; set; }
}
public class GrandExchange
{
public string trend { get; set; }
public string price { get; set; }
}
Then you need to serialize the current item into a Wrapper class
var wrapper = JsonConvert.DeserializeObject<Wrapper>(json);
Then if you want multiple items in a list, you can do so with this code :
// Items to find
int[] itemIds = {1513, 1514, 1515, 1516, 1517};
// Create blank list
List<Item> items = new List<Item>();
foreach (int id in itemIds)
{
var n = new WebClient();
// Get JSON
var json = n.DownloadString(String.Format("http://services.runescape.com/m=itemdb_rs/api/catalogue/detail.json?item={0}", id));
// Parse to Item object
var wrapper = JsonConvert.DeserializeObject<Wrapper>(json);
// Append to list
items.Add(wrapper.item);
}
// Do something with list
It is also worth noting that Jagex limit how many times this API can be called from a certain IP within a time frame, going over that limit will block your IP for a certain amount of time. (Will try and find a reference for this)
I have the following classes:
public class Person
{
public String FirstName { set; get; }
public String LastName { set; get; }
public Role Role { set; get; }
}
public class Role
{
public String Description { set; get; }
public Double Salary { set; get; }
public Boolean HasBonus { set; get; }
}
I want to be able to automatically extract the property value diferences between Person1 and Person2, example as below:
public static List<String> DiffObjectsProperties(T a, T b)
{
List<String> differences = new List<String>();
foreach (var p in a.GetType().GetProperties())
{
var v1 = p.GetValue(a, null);
var v2 = b.GetType().GetProperty(p.Name).GetValue(b, null);
/* What happens if property type is a class e.g. Role???
* How do we extract property values of Role?
* Need to come up a better way than using .Namespace != "System"
*/
if (!v1.GetType()
.Namespace
.Equals("System", StringComparison.OrdinalIgnoreCase))
continue;
//add values to differences List
}
return differences;
}
How can I extract property values of Role in Person???
public static List<String> DiffObjectsProperties(object a, object b)
{
Type type = a.GetType();
List<String> differences = new List<String>();
foreach (PropertyInfo p in type.GetProperties())
{
object aValue = p.GetValue(a, null);
object bValue = p.GetValue(b, null);
if (p.PropertyType.IsPrimitive || p.PropertyType == typeof(string))
{
if (!aValue.Equals(bValue))
differences.Add(
String.Format("{0}:{1}!={2}",p.Name, aValue, bValue)
);
}
else
differences.AddRange(DiffObjectsProperties(aValue, bValue));
}
return differences;
}
If the properties aren't value types, why not just call DiffObjectProperties recursively on them and append the result to the current list? Presumably, you'd need to iterate through them and prepend the name of the property in dot-notation so that you could see what is different -- or it may be enough to know that if the list is non-empty the current properties differ.
Because I don't know how to tell if:
var v1 = p.GetValue(a, null);
is String FirstName or Role Role. I have been trying to find out how to tell if v1 is a String such as FirstName or a class Role. Therefore I won't know when to recursively pass the object property (Role) back to DiffObjectsProperties to iterate its property values.