Adding properties to an object dynamically - c#

I was trying to create objects at runtime. We have .net framework provided classes like DynamicObject and ExpandoObject. Is it possible to create a dynamic object like this
dynamic obj = new expandoObject();
obj["propName1"] = "name"; //string type
obj["propName2"] = 24; //int type
I dont know the property names until runtime. Is it possible to do this way?

Well, two things.
First, yes, you can stuff values into the ExpandoObject object using "property names" contained in strings, because it implements IDictionary<string, object>, so you can do it like this:
void Main()
{
dynamic obj = new ExpandoObject();
var dict = (IDictionary<string, object>)obj;
dict["propName1"] = "test";
dict["propName2"] = 24;
Debug.WriteLine("propName1=" + (object)obj.propName1);
Debug.WriteLine("propName2=" + (object)obj.propName2);
}
Notice how I use the property syntax to retrieve the values there. Unfortunately, dynamic is like a virus and propagates, and Debug.WriteLine is none too happy about dynamic values, so I had to cast to object there.
However, and this is the second thing, if you don't know the property names until runtime, those last two lines there won't appear anywhere in your program. The only way to retrieve the values is again to cast it to a dictionary.
So you're better off just using a dictionary to begin with:
void Main()
{
var obj = new Dictionary<string, object>();
obj["propName1"] = "name";
obj["propName2"] = 24;
Debug.WriteLine("propName1=" + obj["propName1"]);
Debug.WriteLine("propName2=" + obj["propName2"]);
}

Related

How to use dynamic Linq with List object

I have List object of type dynamic like List<dynamic> but when I try to use it with Linq then it's not working. Below is my Linq syntax
var tt = lst.Where(x => (string)x.Type == "test");
So how can I use dynamic Linq on List having type as dynamic object.
It throws an error:
'System.Collections.Generic.List<object>' does not contain a definition for 'Type'
Make sure you have declared the dynamic type correctly or is there any mismatch between the objects. I just tried the same thing and it works for me.
dynamic obj = new {
Data = "sjds"
};
dynamic obj2 = new {
Data = "sjdsf"
};
List<dynamic> dynamics = new List<dynamic>();
dynamics.Add(obj);
dynamics.Add(obj2);
var str = dynamics.Where(x => x.Data == "sjds");
If your case different than this you can share the full code to better understand the scenario.
The compiler has no way to know what your dynamic objects look like. Therefore you need to tell the compiler by casting to a type. Since you're assuming that a particular property exists on your dynamic object (Type), you can try and cast it.
List<YourType> yourList = lst as List<YourType>;
if (yourList!=null){
var filtereList = yourList.Where(x=>x.Type.Equals("test"));
}

Dynamic Expression not working on dynamic objects

I want to dynamically apply a predicates to a list of dynamic object. My solution is working well when I use actual objects but it does not work on dynamic objects and I can't figure out what is the problem.
Note: I searched Stackoverflow none of similar questions are using list of dynamic objects.
I have a list of dynamic objects like the following code. The list contains two dynamic object that have two properties (Name,CreateDate). I used JsonConvert class to create dynamic objects :
var lst = new List<dynamic>();
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("Name", "John");
dict.Add("CreateDate", DateTime.Now);
lst.Add(JsonConvert.DeserializeObject<dynamic>(JsonConvert.SerializeObject(dict)));
dict.Clear();
dict.Add("Name", "sara");
dict.Add("CreateDate", DateTime.Now);
lst.Add(JsonConvert.DeserializeObject<dynamic>(JsonConvert.SerializeObject(dict)));
dict.Clear();
As you see lst is a list of dynamic objects and have 2 items in it.
Now I want to filter list to get the item with the name Jonh (p=> p.Name == "john")
To do this I had the following approach:
ParameterExpression pe = Expression.Parameter(typeof(object), "p");
CallSiteBinder name = Binder.GetMember(CSharpBinderFlags.None, "Name", typeof(object),
new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
var pname = Expression.Dynamic(name, typeof(object), pe);
var right = Expression.Constant("John");
Expression e2 = Expression.Equal(pname, right);
var qu = Expression.Lambda<Func<dynamic, bool>>(e2, pe);
var lst2 = lst.AsQueryable().Where(qu).ToList();// Count()==0 !
The lst2 should contain 1 item but it contains 0 items. But if I change the original list(lst) to a type that has a Name property (let's say List<Person>) it lst2 correctly have 1 item.
UPDATE:
Even when I use ExpandoObject to create dynamic objects it still won't work :
dynamic obj = new ExpandoObject();
var dictionary = (IDictionary<string, object>)obj;
dictionary.Add("Name", "John");
dictionary.Add("CreateDate", DateTime.Now);
UPDATE 2:
As pionted out in the comments ExpandoObject actually works and the problem is with SqlDataReader. Here are what I have tried (see Not working comments in the following code) :
...
List<dynamic> result = new List<dynamic>();
While(dr.Read()){
dynamic obj = new ExpandoObject();
var dictionary = (IDictionary<string, object>)obj;
dictionary.Add("Name","John"); // <= this works fine
// dictionary.Add("Name",dr["Name"]); // <= Not working
// dictionary.Add("Name",dr["Name"].ToItsType()); // <= Not working
// dictionary.Add("Name",dr["Name"].ToString()); // <= Not working
dictionary.Add("CreateDate", DateTime.Now);
result.Add(obj);
}
...
I was able to reproduce the issue (after your UPDATE 2 which gave me the idea) by changing the ExpandoObject example code
dictionary.Add("Name", "John");
to
dictionary.Add("Name", new string("John".ToCharArray()));
to avoid constant string interning, which lead us to the issue in the dynamic expression code.
The dynamic expression type is object, hence Expression.Equal resolves to object operator ==, i.e. ReferenceEquals. That's why the example is working with constant strings and not with runtime created strings.
What you need here is to use actual property type. So simply cast (Expression.Convert) the result of the dynamic property accessor to the expected type:
var pname = Expression.Convert(Expression.Dynamic(name, typeof(object), pe), typeof(string));
Now the expressions which refer to pname expression will resolve with the correct type (in this particular case, Equal will resolve to the overloaded string == operator which correctly compares strings by value. Same for value types like int, DateTime etc.).
dynamic obj = new ExpandoObject();
dictionary.Add("Name", "John");
dictionary.Add("CreateDate", DateTime.Now);
try the above code. Conversion is not required and ExpandoObject should allow to add or remove dynamic objects.
Why not just use dynamic objects instead of dictionary.
Following code works like charm:
var lst = new List<dynamic>();
dynamic obj = new ExpandoObject();
obj.Name = "John";
obj.CreateDate = DateTime.Now;
lst.Add(obj);
obj = new ExpandoObject(); // re-instantiate the obj if you want to differentiate from the List itself
obj.Name = "Sara";
obj.CreateDate = DateTime.Now.AddMonths(-10);
lst.Add(obj);
foreach (var item in lst)
{
Console.WriteLine($"{item.Name} - {item.CreateDate}");
}
You can even filter the list dynamically
Console.WriteLine(lst.Find(i=>i.Name == "John").Name);
Hope it helps.
EDIT
You need to re-instantiate your dynamic obj on each adding. If you dont, your list will have nothing but 2 "Sara"s.
Update
Well, with a little bit work on this this solution got worked for me.
I used JsonConvert.DeserializeObject<ExpandoObject>(...) instead of dynamic. Then wrote a LookUp method for inspecting the element. I think first problem with your code is deserializing your serialized object as dynamic instead of ExpandoObject. After that correction, it was not that hard for the casting dictinaries and getting key-value oriented values.
Here is my code:
var lst = new List<dynamic>();
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("Name", "John");
dict.Add("CreateDate", DateTime.Now);
lst.Add(JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(dict)));
dict.Clear();
dict.Add("Name", "Sara");
dict.Add("CreateDate", DateTime.Now);
lst.Add(JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(dict)));
dict.Clear();
var res = LookUp(lst, "Name", "Sara");
And after that LookUp method
public static object LookUp(List<dynamic> lst, string propName, object value)
{
return lst.FindAll(i =>
{
var dic = i as IDictionary<string, object>;
return dic.Keys.Any(key => dic[key].ToString().Contains(value.ToString()));
});
}
Also if you dont want to cast it to dictionary here is an alternative method for it:
private static object GetProperty(dynamic target, string name)
{
var site =
CallSite<Func<CallSite, dynamic, object>>
.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(CSharpBinderFlags.None, name, target.GetType(),
new[] {CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)}));
return site.Target(site, target);
}
public static object LookUpAlt(List<dynamic> lst, string propName, object value)
{
return lst.FindAll(i => GetProperty(i, propName).Equals(value));
}

Pass a C# dynamic object to a method that takes an IDictionary

Right now, I'm doing this
var data = new JobDataMap(new Dictionary<string,string> { {"obj", "stringify"} });
But I want to do this:
dynamic d = new { obj = "stringify" };
var data = new JobDataMap(d);
Is there some secret syntactical sugar that would allow me to do this?
There's no magical way of doing this. There's no way the compiler can know that your Dynamic object really is a Dictionary at compile time.
That being said, you could create an extension method that converts it to a Dictionary so that you could do something like this:
dynamic d = new { obj = "stringify" };
var data = new JobDataMap(d.ToDictionary());
This blogpost offers an example: http://blog.andreloker.de/post/2008/05/03/Anonymous-type-to-dictionary-using-DynamicMethod.aspx

Assign the literal string as a property of a dynamic object during runtime and access it

How can I assign the fieldname of a sqldatareader during runtime dynamically to a dynamic object?
Lets assume I have read the fieldname of a SqlDataReader into a variable:
string sqlDataReaderFieldNameStringVariable = reader.GetName(index);
I can not say:
dynamic dyn = new ExpandoObject();
dyn.sqlDataReaderFieldNameStringVariable = "test";
How can I do that?
UPDATE:
still time to get a point ;-) I add my dyn object to a List of type ExpandoObject which is the return value of a method. When I access the list via data[0].test property does not exist while compile time ???
When I do this outside of the method returning the List:
dynamic bla = (ExpandoObject)data[0];
String shit = bla.Name;
Why do I have to cast it? Any workaround? Thanks Jon.
You have to cast your ExpandoObject dyn to IDictionary<string, object> first to do that:
dynamic dyn = new ExpandoObject();
var dynDict = dyn as IDictionary<string, object>;
dynDict[sqlDataReaderFieldNameStringVariable] = "test";
For most dynamic objects, it's tricky. Doable (using IDynamicMetaObjectProvider) but tricky. If you're really using ExpandoObject, it's simple because that implements IDictionary<string, object>:
dynamic dyn = new ExpandoObject();
var dictionaryView = (IDictionary<string, object>) dyn;
dictionaryView[sqlDataReaderFieldNameStringVariable] = "test";

how to add an item to a object initialized with { blah = "asdf" }

how to add an item to an object initialized with:
object obj = new { blah = "asdf" };
If I want to add another key value pair, how would i?
You can't modify the object's anonymous type definition once you make the object using that initializer syntax. That is, once you initialize it with { blah = "asdf" }, it only has that blah property. You can't add another. This is because anonymous types are static types.
The ExpandoObject answers work though, for a dynamic object. See the other answers for that.
If you're really just trying to manage a collection of key-value pairs (kinda sorta based on the way you phrased your question), use a dictionary.
var kvp = new Dictionary<string, string>
{
{ "blah", "asdf" }
};
kvp.Add("womp", "zxcv");
#BoltClock is right on. Another alternative is to use an ExpandoObject, at the loss of intellisense.
dynamic obj = new ExpandoObject();
obj.blah = "asdf";
// sometime later
obj.somethingelse = "dfgh";
// obj now has 'blah' and 'somethingelse' 'properties'
Once you define an object like that, you're done. You can't add anything to it.
If you're using C# 4.0, though, you could always use a dynamic type:
dynamic obj = new ExpandoObject();
obj.blah = "asdf";
obj.blahBlah = "jkl;";

Categories