How to clone an ExpandoObject - c#

This is for a MVVM based WPF project:
I am using an ExpandoObject in a view model for a dialog, which works very nicely since it implements INotifyPropertyChanged and I can bind to properties of the object directly in XAML.
However, to account for the user manipuating data but then hitting cancel I need to make a copy of the ExpandoObject to restore the original content.
In the dialog no properties are added to the object.
How can I clone it?

Shallow copy:
static ExpandoObject ShallowCopy(ExpandoObject original)
{
var clone = new ExpandoObject();
var _original = (IDictionary<string, object>)original;
var _clone = (IDictionary<string, object>)clone;
foreach (var kvp in _original)
_clone.Add(kvp);
return clone;
}
Deep copy:
static ExpandoObject DeepCopy(ExpandoObject original)
{
var clone = new ExpandoObject();
var _original = (IDictionary<string, object>)original;
var _clone = (IDictionary<string, object>)clone;
foreach (var kvp in _original)
_clone.Add(kvp.Key, kvp.Value is ExpandoObject ? DeepCopy((ExpandoObject)kvp.Value) : kvp.Value);
return clone;
}

As a remaining believer in static typing, eugh...
That being said, it looks like ExpandoObject implements IDictionary<string, object>:
dynamic foo1d = new ExpandoObject();
foo1d.a = "test";
dynamic foo2d = new ExpandoObject();
foreach (var kvp in (IDictionary<string, object>)foo1d)
{
((IDictionary<string, object>)foo2d).Add(kvp);
}
Debug.Assert(foo1d.a == foo2d.a);
or in VB:
Dim foo1d As Object = New ExpandoObject
Dim foo2d As Object = New ExpandoObject
foo1d.a = "foo"
Dim cloneDictionary = CType(foo2d, IDictionary(Of String, Object))
For Each line In CType(foo1d, IDictionary(Of String, Object))
cloneDictionary.Add(line.Key, line.Value)
Next
I will note that this is only a shallow clone. You would have to do further work do clone referenced objects.

var ExpandoObjs = GetDynamicList();
//clone
var clonedExpandos = ExpandoObjs.Cast<dynamic>().Select(x => x).ToList();
//modify and sort
var transformedExpandos = ExpandoObjs.Cast<dynamic>().Select(x =>
{
x.url= x.url + " some more stuff";
return x;
}).OrderBy(x => x.order).ToList();
as long as url is a property of the dynamic object, this will work.
It's useful if you're consuming a service call that doesn't have concrete types and you want to modify/sort the result.

Related

Adding an attribute to a property of a dynamic object

I want to create a dynamic object based in other object's properties and attributes, but I don't know neither how to add attributes to my dynamic properties, nor how to get them back.
Is that possible?
Here is my code, but it's not working. Thanks!
var expando = new ExpandoObject() as IDictionary<String, Object>;
var props = typeof(Costumer).GetProperties();
foreach (var prop in props)
{
var customAttributes = prop.GetCustomAttributes(true);
var displayAttr = customAttributes.OfType<DisplayAttribute>();
expando.Add(prop.Name, prop.PropertyType);
TypeDescriptor.AddAttributes(expando[prop.Name],displayAttr.SingleOrDefault());
var getAttribute = TypeDescriptor.GetAttributes(expando[prop.Name]);
}

How do I use MemoryCache to speedup translation of various objects into strings?

I have a large dataset (IEnumerable of [Table]-attributed class objects) from a Linq-To-Sql query and I need to produce a CSV file from it. I loop over the dataset and for each item I convert the value of each property of the item into a string using various formatting options.
Type t = typeof(T);
var properties = t.GetProperties();
foreach (var item in list)
{
foreach (var property in properties)
{
// This is made using delegates, but whatever
object value = property.GetValue(item, null);
// convert to string and feed to StringBuilder
}
}
The problem is that conversion takes even longer that running the query. The dataset contains heavily denormalized data - numerous items have the same properties having the same values and only some properties having different values. Each property value is translated separately for each item in the dataset. So my code converts the same data into the same strings - over and over. And I'd like to somehow speed this up, preferable without changing the SQL query.
Looks like MemoryCache class could work, but I need to craft unique keys for each object. I can't figure out how I could craft such keys reliably and efficiently enough.
How do I make use of MemoryCache so that I can cache translation results for objects of different types?
If you just want to speed it up I would suggest ExpressionTrees more than MemoryCache. This assumes you don't have nested objects to want to read and I can use reflection on the first item and it will be the same for each item in the IEnumerable - which from your example code in the question seems correct.
Also if it's big and you are going to just write it out to a file I would suggest going straight to a FileStream instead of a StringBuilder.
public class CSV
{
public static StringBuilder ToCSV(IEnumerable list)
{
Func<object, object[]> toArray = null;
var sb = new StringBuilder();
// Need to initialize the loop and on the first one grab the properties to setup the columns
foreach (var item in list)
{
if (toArray == null)
{
toArray = ItemToArray(item.GetType());
}
sb.AppendLine(String.Join(",", toArray(item)));
}
return sb;
}
private static Func<object, object[]> ItemToArray(Type type)
{
var props = type.GetProperties().Where(p => p.CanRead);
var arrayBody = new List<Expression>();
// Create a parameter to take the item enumeration
var sourceObject = Expression.Parameter(typeof (object), "source");
// Convert it to the type that is passed in
var sourceParam = Expression.Convert(sourceObject, type);
foreach (var prop in props)
{
var propType = prop.PropertyType;
if (IsValueProperty(propType))
{
// get the value of the property
Expression currentProp = Expression.Property(sourceParam, prop);
// Need to box to an object if value type
if (propType.IsValueType)
{
currentProp = Expression.TypeAs(currentProp, typeof (object));
}
// Add to the collection of expressions so we can build the array off of this collection
arrayBody.Add(currentProp);
}
}
// Create an array based on the properties
var arrayExp = Expression.NewArrayInit(typeof (object), arrayBody);
// set a default return value of null if couldn't match
var defaultValue = Expression.NewArrayInit(typeof (object), Expression.Constant(null));
//Set up so the lambda can have a return value
var returnTarget = Expression.Label(typeof (object[]));
var returnExpress = Expression.Return(returnTarget, arrayExp, typeof (object[]));
var returnLabel = Expression.Label(returnTarget, defaultValue);
//Create the method
var code = Expression.Block(arrayExp, returnExpress, returnLabel);
return Expression.Lambda<Func<object, object[]>>(code, sourceObject).Compile();
}
private static bool IsValueProperty(Type propertyType)
{
var propType = propertyType;
if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof (Nullable<>))
{
propType = new NullableConverter(propType).UnderlyingType;
}
return propType.IsValueType || propType == typeof (string);
}
}

pass class type as parameter c# and used with dictionary

I am create a function to return a dictionary and I want to pass class name through as a parameter. But its gives an error. Code i have written is given below
public Dictionary<object, object> GetDetails(Type Classname)
{
MvcDemoDBEntities db = new MvcDemoDBEntities();
Dictionary<Classname, object> dict = new Dictionary<Classname, object>();
var data = (from h in db.People
select h).ToList();
foreach (var item in data)
{
dict.Add(data, true);
}
return dict;
}
what am i doing wrong
and i would like to call this function with a class name dynamically, like this :
List<people> list = GetDetails(people).Keys.ToList();
people its my class name.
Using Generics
Your current approach will give you much trouble. As you are going to pass a Type object for your class, you will need reflection to be able to create the Dictionary.
As an alternative, I propose to you to create a generic method:
public Dictionary<object, object> GetDetails<TClass>()
{
MvcDemoDBEntities db = new MvcDemoDBEntities();
Dictionary<TClass, object> dict = new Dictionary<TClass, object>();
var data = (from h in db.People
select h).ToList();
foreach (var item in data)
{
dict.Add(data, true);
}
return dict;
}
Use it like this:
List<people> list = GetDetails<people>().Keys.ToList();
Using Type
Of course, this can be done using a Type object, this requiers the use of reflection to be able to create an object of which type we don't know (that object is the dictionary). This is done as follows:
public Dictionary<object, object> GetDetails(Type Class)
{
//Null check
if (null == Class)
{
throw new ArgumentNullException("Class");
}
MvcDemoDBEntities db = new MvcDemoDBEntities();
//Get the generic dictionary type:
Type DictType = typeof(Dictionary<,>).MakeGenericType(Class, typeof(object));
//Create the dictionary object:
object dict = Activator.CreateInstance(typeof(DictType));
//Get the Add method:
var add = DictType.GetMethod("Add", new Type[]{Class, typeof(object)});
var data = (from h in db.People
select h).ToList();
foreach (var item in data)
{
//add to the dictionary:
add.Invoke(dict, new object[]{item, true});
}
return dict;
}
Use like this:
List<people> list = GetDetails(typeof(people)).Keys.ToList();
Digging deeper
I notice you have this line:
var data = (from h in db.People select h).ToList();
You may be interested in changing People to a property matching the name of the class you pass in. This can only be archived via reflection. In a similar way as how we got the Add method of the dictionary, we can get a property from the object db which name is given by the argument type.
I'll present this as a second method to be called by the first.
Using Generics
public IEnumerable<TClass> GetCollection<TClass>(MvcDemoDBEntities db)
{
//get the type of db
var dbType = db.GetType();
//get the name of the type TClass
var name = typeof(TClass).Name;
//get the property
var prop = dbType.GetProperty(name);
//read the property and return
return prop.GetValue(db);
}
To use, replace this:
var data = (from h in db.People select h).ToList();
With this:
var data = (from h in GetCollection<TClass>(db) select h).ToList();
Using Type
Here the struggle is that we don't know the item type... so I'll use IEnumerable.
public IEnumerable GetCollection(MvcDemoDBEntities db, Type Class)
{
//get the type of db
var dbType = db.GetType();
//get the name of the type Class
var name = Class.Name;
//get the property
var prop = dbType.GetProperty(name);
//read the property and return
return prop.GetValue(db);
}
To use, replace this:
var data = (from h in db.People select h).ToList();
With this:
var data = (from h in GetCollection(db, Class).Cast<object>() select h).ToList();
You need to use typeof to pass the type of your class
List<people> list = GetDetails(typeof(people)).Keys.ToList();

Creating Dynamic properties for object

I have a problem in creating dynamic objects. Please find the below code,
List<object> membersList = new List<object>();
foreach(var members in activityMembers){
dynamic myObject = new System.Dynamic.ExpandoObject();
myObject.MemberNumber = members.MemberNumber;
myObject.MemberName = members.Name;
foreach (var activity in members.ActivityList)
{
myObject.[activity.ActivityName] = activity.Minutes;
}
membersList.Add(myObject);
}
there inside the second foreach loop, i need to generate the properties to all activities. for example if there are 4 activities in members.ActivityList, then 4 properties needs to be generated for object.
myObject.Activity1 = 10;
myObject.Activity2 = 20;
myObject.Activity3 = 30;
myObject.Activity4 = 40;
How can i do this? What i did wrong here?
Regards,
Karthik.
Remove the . when you are indexing the object i.e. change
myObject.[activity.ActivityName] = activityMinutes;
to
myObject[activity.ActivityName] = activity.Minutes;
Actually this won't solve your problem straight away, it will compile fine but when you attempt to run it will throw a RuntimeBinderException as you can't index into a ExpandoObject directly. You need to cast it as a dictionary before iterating (that's effectively what it is) e.g.
var dict = (IDictionary<string, object>)myObject;
...
dict[activity.ActivityName] = activity.Minutes;
I suspect you need to treat the ExpandoObject as a dictionary for that part:
IDictionary<string, object> dictionary = myObject;
foreach (var activity in members.ActivityList)
{
dictionary[activity.ActivityName] = activity.Minutes;
}
That's the way of assigning properties to an ExpandoObject when you don't know the property name at compile-time.

Dynamically removing a member from Expando /dynamic object [duplicate]

Say I have this object:
dynamic foo = new ExpandoObject();
foo.bar = "fizz";
foo.bang = "buzz";
How would I remove foo.bang for example?
I don't want to simply set the property's value to null--for my purposes I need to remove it altogether. Also, I realize that I could create a whole new ExpandoObject by drawing kv pairs from the first, but that would be pretty inefficient.
Cast the expando to IDictionary<string, object> and call Remove:
var dict = (IDictionary<string, object>)foo;
dict.Remove("bang");
You can treat the ExpandoObject as an IDictionary<string, object> instead, and then remove it that way:
IDictionary<string, object> map = foo;
map.Remove("Jar");
MSDN Example:
dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
((IDictionary<String, Object>)employee).Remove("Name");
You can cast it as an IDictionary<string,object>, and then use the explicit Remove method.
IDictionary<string,object> temp = foo;
temp.Remove("bang");

Categories

Resources