Create .NET object with DataContract from a dictionary - c#

Having a class defined:
[DataContract]
public class Command
{
[DataMember(Name = "cmdName")]
public string CommandName { get; set; }
[DataMember(Name = "cmdTransactionId")]
public string CommandTransactionId { get; set; }
}
I would like to create an instance of that class from a dictionary:
Dictionary<string, object> propertyBag = new Dictionary<string, object>();
propertyBag["cmdName"] = "cmd1";
propertyBag["cmdTransactionId"] = "1";
Command command = deserializer.Create<Command>(propertyBag);
DataContractSerializer is not working for me nor is the JavaScriptSerializer.ConvertToType as each of them is missing a piece that stop s me from creating objects in one go.

JavaScriptSerializer will work here with some changes:
var propertyBag = new Dictionary<string, object>();
propertyBag["CommandName"] = "cmd1";
propertyBag["CommandTransactionId"] = "1";
var serializer = new JavaScriptSerializer();
var res = serializer.Serialize(propertyBag);
var command = serializer.Deserialize<Command>(res);
I used Deserialize method instead of ConvertToType one. The second difference is more significant. A dictionary used by me contains keys which are the same as names of properties in Command class. If you don't like it you should try to write a custom JavaScriptConverter. See this question.

Maybe you can try this code for deserializer.Create<Command>(propertyBag):
public T Create<T>(IDictionary<string, object> propertyBag)
{
var result = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (var item in from member in typeof(T).GetMembers()
let dataMemberAttr = member.GetCustomAttributes(typeof(DataMemberAttribute), true).Cast<DataMemberAttribute>().SingleOrDefault()
where dataMemberAttr != null && propertyBag.ContainsKey(dataMemberAttr.Name)
select new { Member = member, Value = propertyBag[dataMemberAttr.Name] })
{
var property = item.Member as PropertyInfo;
if (property != null)
{
property.SetValue(result, item.Value, null);
continue;
}
var field = item.Member as FieldInfo;
if (field != null)
{
field.SetValue(result, item.Value);
continue;
}
}
return result;
}
This code is not tested - just typed here.

Related

Getting DisplayAttribute when itering on all enums of an assembly

I'm trying to iterate through all the enums contained in an assembly, and for each of them, build an object which I will return to a frontend.
I can retrieve the text value of a enum item, as well as its numeric value, but I'm trying to get its DisplayAttribute when there is one. (ie: [Display(Name = "Sales Admin")] instead of SalesAdmin)
Here's what I have so far
//represents an enum, that will be consumed by the frontend
public class DataEnumItem
{
public string EnumName { get; set; }
public Dictionary<string, int> Items { get; set; } = new Dictionary<string, int>();
}
private List<DataEnumItem> ReturnDataEnumItems<T>()
{
List<DataEnumItem> results = new List<DataEnumItem>();
var dataEnums = Assembly.GetAssembly(typeof(T)).GetTypes().Where(x => x.IsEnum && x.IsPublic);
foreach (var e in dataEnums)
{
var values = Enum.GetValues(e);
DataEnumItem item = new DataEnumItem();
item.EnumName = e.Name;
foreach (var value in values)
{
item.Items.Add(value.ToString(), (int)value);
}
results.Add(item);
}
return results;
}
So far, that works, but like I said, I wish I could get the DisplayAttribute on each of the enum's item if there is one.
I have a method to do that, but only when I have a property that is strongly typed. For example:
enum MyEnum {
[Display(Name = "First Choice")]
firstChoice = 0
}
public static string GetDisplay(this Enum enumValue)
{
var displayAttr = GetAttribute<DisplayAttribute>(enumValue);
if (displayAttr != null)
return displayAttr.Name;
else
return "";
}
Then using it:
MyEnum x = MyEnum.firstChoice;
string display = x.GetDisplay();
Is there any to retrieve the display attribute when the enum value I have is not strongly typed (ie: retrieved via Enum.GetValues())
You can cast to System.Enum and call your GetDisplay method:
foreach (var e in dataEnums)
{
DataEnumItem item = new DataEnumItem();
item.EnumName = e.Name;
item.Items = Enum.GetValues(e)
.Cast<Enum>()
.Select(ev => (key: ev.GetDisplay(), value: (int)(object)ev))
.ToDictionary(ev => ev.key, ev => ev.value);
results.Add(item);
}
Or just substitute you dictionary addition with:
item.Items.Add(((Enum)value).GetDisplay(), (int)value)
Or, if previous options for some reason does not work for you:
e.GetField(value.ToString()).GetCustomAttributes<DisplayAttribute>()

create objects from properties in string

object with additional properties from json-like notation string should created.
Method will called from Razor view to pass colmodel to jqgrid
as json object like
#Html.Raw( Json.Encode( Model.GetColModel()))
Method should have signature like
object GetColModel(string colName, int colWidth, string additonalProperties)
For example,
GetColModel("customer", 17, "address=\"Toronto\", index=1555" )
should return object
new { colName="customer", colwidth=17, address="Toronto", index=1555 }
There may be nested properties like in JSON, eq.
GetColModel("customer", 17, "formatoptions= new { formatter=\"number\", editable=true } " )
should return object
new { colName="customer", colwidth=17, formatoptions=new {
formatter="number",
editable=true
}
}
I tried method
public object GetColModel(string colName, int colWidth, string additonalProperties)
{
return new
{
name = colName,
width = colWidth,
&addtitionalProperties
};
}
but this fails since macros are not supported in C#
How to create such method or other way to add properties from database to json strung in Razor view ?
It is called from ASP.NET/Mono C# MVC 4 viewmodel.
Razor views and RazorEngine are used.
There is nothing built in to do this, but you parse your string using string (string.Split will let you split on ',' but if you might have those in your text you will have to build a real parser, or switch you string format to something like CSV where you can find lots of parsers out there. You might be able to find a property parser for simple syntax. Or you push your additional properties string as json and use Json.net to parse.
Once you have your string parsed into a key/value structure, then you can use ExpandoObject to populate your final object and return that.
https://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject(v=vs.110).aspx
Here is a naive implementation of a true json based solution.
You can invoke it using:
dynamic d = Model.GetColModel("customer", 17, " { formatoptions : { formatter : \"number\", editable :true }, stam :2}");
Implementation:
static class ModelExtension
{
public static dynamic GetColModel(this Model model, string colName, int colWidth, string additonalProperties) {
dynamic expando = new ExpandoObject();
var json = JsonConvert.DeserializeObject<JObject>(additonalProperties);
expando.name = colName;
expando.width = colWidth;
return new FromPropertiesDynamicObjectCreator(expando, json);
}
private class FromPropertiesDynamicObjectCreator : DynamicObject
{
private readonly dynamic expando = null;
public FromPropertiesDynamicObjectCreator(IDictionary<string, object> expando, JObject props = null) {
this.expando = expando;
if (props != null) {
((dynamic)this).props = props;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value) {
if (binder.Name.Equals("props")) {
var jsonObj = value as JObject;
JToken current = jsonObj.First;
var dictionary = expando as IDictionary<string, object>;
RecurseJson(current, dictionary);
return true;
}
return false;
}
private void RecurseJson(JToken current, IDictionary<string, object> dictionary) {
JToken value;
Dictionary<string, object> newDictionary;
while (current != null) {
var children = current.Children().ToList();
foreach (var child in children) {
switch (child.Type) {
case JTokenType.Object:
case JTokenType.Array:
newDictionary = new Dictionary<string, object>();
dictionary[child.Path] = newDictionary;
RecurseJson(child, newDictionary);
break;
case JTokenType.Property:
var prop = ((JProperty)child);
value = prop.Value;
if (value.HasValues) {
newDictionary = new Dictionary<string, object>();
dictionary[prop.Name] = newDictionary;
RecurseJson(child, newDictionary);
break;
}
dictionary[prop.Name] = ((dynamic)value).Value;
break;
default:
var val = ((dynamic)child).Value;
if (val is JToken) {
dictionary[child.Path] = val.Value;
}
else {
dictionary[child.Path] = val;
}
break;
}
}
current = current.Next;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result) {
object value;
var dictionary = expando as IDictionary<string, object>;
if (dictionary.TryGetValue(binder.Name, out value)) {
var innerDictionary = value as IDictionary<string, object>;
if (innerDictionary != null) {
result = new FromPropertiesDynamicObjectCreator(innerDictionary);
}
else {
result = value;
}
return true;
}
result = null;
return true;
}
}
}

How do I convert from dynamic object to specified Type?

Having a model of type Dictionary<string,dynamic> and would like to convert it to Dictionary<string, MyType1> or Dictionary<string, MyOtherType>!
I've tried
var converted = (Dictionary<string,MyType1>)model
without success tried
IConvertible iConv = model; var converted = iConv.ToType(typeof(MyOtherType), null);
too but it doesn't work
Exception: Cannot convert system.object to type x
How do I convert from runtime type (dynamic) to a well known Type?
There is no built-in conversion from one dictionary type to another dictionary type. However, using Enumerable.ToDictionary, you can easily create a new dictionary from any other data structure.
In your particular example, you can use it as
var converted = model.ToDictionary(kv => kv.Key, kv => (MyType1) kv.Value);
Of course this will throw an exception if your values aren't actually of type MyType1. If they aren't, then instead of (MyType1) kv.Value, call some custom conversion function at that point.
The following little demo works for simple types:
MapDynamicToDictionary test shows turning the dynamic to a dictionary.
MapDictionaryToType shows converting the dictionary to a type T.
You could improve on this by doing checks for types or using as etc.
public class Test
{
[Fact]
public void MapDynamicToDictionary()
{
dynamic d = new { Nr = 1, Name = "Devon" };
var dictionary = TurnObjectIntoDictionary(d);
Assert.Equal(2, dictionary.Keys.Count);
}
[Fact]
public void MapDictionaryToType()
{
dynamic d = new { Nr = 1, Name = "Devon" };
var dictionary = TurnObjectIntoDictionary(d);
var instance = new MyType();
Map(dictionary, instance);
Assert.Equal(instance.Nr, 1);
Assert.Equal(instance.Name, "Devon");
}
public static void Map<T>(IDictionary<string, object> dictionary, T instance)
{
var attr = BindingFlags.Public | BindingFlags.Instance;
foreach (var prop in instance.GetType().GetProperties(attr))
{
if (prop.CanWrite)
{
if(dictionary.ContainsKey(prop.Name))
{
var v = Convert.ChangeType(dictionary[prop.Name], prop.PropertyType);
prop.SetValue(instance, v); }
}
}
}
public static IDictionary<string, object> TurnObjectIntoDictionary(object data)
{
var attr = BindingFlags.Public | BindingFlags.Instance;
var dict = new Dictionary<string, object>();
foreach (var prop in data.GetType().GetProperties(attr))
{
if (prop.CanRead)
{
dict.Add(prop.Name, prop.GetValue(data, null));
}
}
return dict;
}
}
class MyType
{
public int Nr { get; set; }
public string Name { get; set; }
}
Could use TypeConverter to handle more complex examples. Nice example here: http://putridparrot.com/blog/type-conversions-in-c/
I would put a static constructor on your well known type, which accepts dynamic, and build the well known type from that. e.g.
public class SomeType
{
public static SomeType FromDynamic(dynamic arg)
{
return new SomeType
{
SomeProperty = arg.SomeProp
}
}
public int SomeProperty {get; set; }
}
Then you'd just have to iterate over your Dictionary<string,dynamic> and build up the new object like:
var dictionary = new Dictionary<string, SomeType>();
foreach(var item in model)
{
dictionary.Add(item.Key, SomeType.FromDynamic(item.Value));
}
Or borrowing from #hvd:
var converted = model.ToDictionary(kv => kv.Key, kv => SomeType.FromDynamic(kv.Value));

Select new {Unknown fields} from IEnumerable<UnknownType>

I am using VS2010 and EF4.0. The goal is to select fields of any IEnumerable, in order to show in the DataGridView. Take Northwind.Employees as example, the following code is OK.
private void button1_Click(object sender, EventArgs e)
{
NorthwindEntities en = new NorthwindEntities();
dataGridView1.DataSource = SelectNew(en.Employees, new string[] { "EmployeeID", "FirstName" });
}
public object SelectNew(object items, string[] fields)
{
IEnumerable<Employee> ems = items as IEnumerable<Employee>;
return ems.Select(em => new
{
id = em.EmployeeID,
name = em.FirstName
}
).ToArray();
}
The parameter object items is IEnumerable of EntityObject, and the function will be executed at client side memorry and shall have nothing to do with database now.
But I don't know the EntityObject type (Employee) until runtime, so maybe some complex reflection will be used.
I have checked this,
but when I bind the result to the control, it showed only blank rows without any column or data. And the funciton is for IQueryable, I have tried IEnumerable.AsQueryable and pass to it, but the results did not show any column either.
I've modified the example I pointed to in my comment above. This actually returns an IEnumerable<Dictionary<string,object>>, where each Dictionary represents one of the "new objects", and each key value pair in the dictionary represents a property and its value. Perhaps you can modify this for your use?
I'm not sure if you can simply bind the result to the DataGrid, but you should be able to figure it out.
I don't believe it's possible to create an anonymous type on the fly... But it might be possible to change this to use a dynamic type like ExpandoObject instead of a Dictionary. See this question for some hints on how to do that. I've never used dynamic objects, so you're on your own there!
public class TestClassA {
public string SomeString { get; set; }
public int SomeInt { get; set; }
public TestClassB ClassB { get; set; }
}
public class TestClassB {
public string AnotherString { get; set; }
}
public class Program {
private static void Main(string[] args) {
var items = new List<TestClassA>();
for (int i = 0; i < 9; i++) {
items.Add(new TestClassA {
SomeString = string.Format("This is outer string {0}", i),
SomeInt = i,
ClassB = new TestClassB { AnotherString = string.Format("This is inner string {0}", i) }
});
}
var newEnumerable = SelectNew(items, new string[] { "ClassB.AnotherString" });
foreach (var dict in newEnumerable) {
foreach (var key in dict.Keys)
Console.WriteLine("{0}: {1}", key, dict[key]);
}
Console.ReadLine();
}
public static IEnumerable<Dictionary<string, object>> SelectNew<T>(IEnumerable<T> items, string[] fields) {
var newItems = new List<Dictionary<string, object>>();
foreach (var item in items) {
var dict = new Dictionary<string, object>();
foreach (var field in fields)
dict[field] = GetPropertyValue(field, item);
newItems.Add(dict);
}
return newItems;
}
private static object GetPropertyValue(string property, object o) {
if (property == null)
throw new ArgumentNullException("property");
if (o == null)
throw new ArgumentNullException("o");
Type type = o.GetType();
string[] propPath = property.Split('.');
var propInfo = type.GetProperty(propPath[0]);
if (propInfo == null)
throw new Exception(String.Format("Could not find property '{0}' on type {1}.", propPath[0], type.FullName));
object value = propInfo.GetValue(o, null);
if (propPath.Length > 1)
return GetPropertyValue(string.Join(".", propPath, 1, propPath.Length - 1), value);
else
return value;
}
}

How to get property name and value from generic model with generic list?

Using the following model as an example.
public class FooModel
{
public FooModel()
{
Bars= new List<BarModel>();
}
[ManyToMany]
public IList<BarModel> Bars{ get; set; }
}
public class BarModel
{
public int Id { get; set; }
}
I need to extrapolate the List<BarModel> from a fooModel object, and build up a Dictionary<string, object> from each BarModel in the list.
Let's say I create the following object.
var fooModel = new FooModel();
var bar1 = new BarModel {Id = 1};
var bar2 = new BarModel {Id = 2};
fooModel.Bars = new List<BarModel>{bar1,bar2};
And now I want to get all properties within Foo that have the [ManyToMany] attribute.
// First I call the method and pass in the model
DoSomething(fooModel);
// Next I extract some values (used elsewhere)
public DoSomething<TModel>(IModel model){
var dbProvider = ...;
var mapper = new AutoMapper<TModel>();
var tableName = GetTableName( typeof( TModel ) );
UpdateJoins( dbProvider, fooModel, tableName, mapper );
}
// Finally I begin dealing with the collection.
private static void UpdateJoins<TModel> ( IDbProvider dbProvider, TModel model, string tableName, IAutoMapper<TModel> mapper ) where TModel : class, new()
{
foreach (
var collection in
model.GetType()
.GetProperties()
.Where( property => property.GetCustomAttributes( typeof( ManyToManyAttribute ), true ).Any() ) )
{
if ( !IsGenericList( collection.PropertyType ) )
throw new Exception( "The property must be a List" );
// Stuck Here - pseudo code
//====================
foreach (loop the collection)
var collectionName = ...; // Bar
var nestedPropertyName = ...; // Id
var rightKey = collectionName + nestedPropertyName; // BarId
var nestedPropertyValue = ...; // 1
}
}
In the example above, the OUTER foreach is only going to run ONCE because there is only one Property within FooModel that is decorated with the [ManyToMany] attribute.
Therefore PropertyInfo property is a List<BarModel>
How do I do the above INNER foreach and extract the required data?
This may get you on the right track. The idea is if you encounter a [ManyToMany] / generic list you reflect it using recursive call to the same method and then flatten the returned values to form a unique key. You probably will need to tweak it to suit your problem. The below code returns a dictionary with formatted key strings built from collection names, indexes and property names. E.G:
Bars[0].Id = 1
Bars[1].Id = 2
Code:
//This is just a generic wrapper for the other Reflect method
private static Dictionary<string, string> Reflect<TModel>(TModel Model)
{
return Reflect(Model.GetType(), Model);
}
private static Dictionary<string, string> Reflect(Type Type, object Object)
{
var result = new Dictionary<string, string>();
var properties = Type.GetProperties();
foreach (var property in properties)
{
if (
property.GetCustomAttributes(typeof(ManyToManyAttribute), true).Any() &&
property.PropertyType.GetGenericTypeDefinition() == typeof(IList<>))
{
var genericType = property.PropertyType.GetGenericArguments().FirstOrDefault();
var listValue = (IEnumerable)property.GetValue(Object, null);
int i = 0;
foreach (var value in listValue)
{
var childResult = Reflect(genericType, value);
foreach (var kvp in childResult)
{
var collectionName = property.Name;
var index = i;
var childPropertyName = kvp.Key;
var childPropertyValue = kvp.Value;
var flattened = string.Format("{0}[{1}].{2}", collectionName, i, childPropertyName);
result.Add(flattened, childPropertyValue);
}
i++;
}
}
else
{
result.Add(property.Name, property.GetValue(Object, null).ToString());
}
}
return result;
}

Categories

Resources