Dynamic Expression not working on dynamic objects - c#

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));
}

Related

Dynamically creating an object with dynamic children using data at runtime with ExpandoObject in C#

I've created a dynamic object and set properties and values to it at runtime using ExpandoObject
dynamic parentDynamic = new ExpandoObject();
var parentName = "GroupOne";
((IDictionary<String, Object>)parentDynamic)[parentName] = "default";
Console.WriteLine(parentDynamic.GroupOne);
The Console successfully outputs "default" as expected.
I've also created a child object with multiple properties in the same manner
dynamic childDynamic = new ExpandoObject();
var childProperty1 = "FirstName";
var childProperty2 = "LastName";
var childProperty3 = "Occupation";
((IDictionary<String, Object>)childDynamic)[childProperty1] = "John";
((IDictionary<String, Object>)childDynamic)[childProperty2] = "Smith";
((IDictionary<String, Object>)childDynamic)[childProperty3] = "Plumber";
Console.WriteLine(childDynamic.Occupation);
The Console successfully outputs "Plumber" as expected.
Where I am getting in a jam is when I attempt to add the childDynamic object to the parentDynamic object and give it a name at runtime. Here is my latest failed attempt:
var childName = "ChildOne";
((IDictionary<String, Object>)((IDictionary<String, Object>)parentDynamic)[parentName])[childName] = childDynamic;
Console.Write(parentDynamic.GroupOne.ChildOne.Occupation);
The error I am getting when attempting the assignment is: Unable to cast object of type 'System.String' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'.
Essentially I would like to be able access parentDynamic.GroupOne.ChildOne.Occupation and get back "Plumber" or parentDynamic.GroupOne.ChildOne.FirstName and get back "John"
Originally I was trying to make my assignments all at once like so
parentDynamic["GroupOne"]["ChildOne"]["Occupation"] = "Plumber"
But I get the error Cannot apply indexing with [] to an expression of type 'System.Dynamic.ExpandoObject' Which is why I went down the path of creating a parent and child object and casting them as Dictionary objects first. Ideally I would like to just do something like the above as it's MUCH simpler.
In order to be able to use parentDynamic.GroupOne.ChildOne syntax, GroupOne property should also be dynamic ExpandoObject while in your case it is a string.
Something like this:
dynamic parentDynamic = new ExpandoObject();
parentDynamic.GroupOne = new ExpandoObject();
parentDynamic.GroupOne.ChildOne = new ExpandoObject();
parentDynamic.GroupOne.ChildOne.FirstName = "John";
parentDynamic.GroupOne.ChildOne.LastName = "Smith";
parentDynamic.GroupOne.ChildOne.Occupation = "Plumber";
or with IDictionary<string, object> casts:
IDictionary<string, object> parent = new ExpandoObject();
IDictionary<string, object> group = new ExpandoObject();
IDictionary<string, object> child = new ExpandoObject();
child["FirstName"] = "John";
child["LastName"] = "Smith";
child["Occupation"] = "Plumber";
parent["GroupOne"] = group;
group["ChildOne"] = child;
dynamic parentDynamic = parent;
Console.WriteLine(parentDynamic.GroupOne.ChildOne.Occupation);

how to convert dynamic to list<dynamic>

i have a variable swimlaneAttribute:
List<dynamic> swimlaneAttributes = new List<dynamic>();
but in a function i have a return type of dynamic
public dynamic GetSwimlaneAttribute(List<ProjectSwimlaneAttribute> swimlaneAttributeTable, Dictionary<string, string> dic)
{
dynamic swimlaneAttributes = null;
swimlaneAttributes = swimlaneAttributeTable.Select(s => new
{
ID = s.Id,
DataType = s.AttributeDataType,
IsCriticalField = s.IsCriticalField,
});
return swimlaneAttributes;
}
this will return some records from table parameter that i am passing!!
now i have to call this GetSwimlaneAttribute function, in return i will get all the required records(from a table)
but when i pass this to swimlaneAttributes it goes to catch block!!!
swimlaneAttributes = GetSwimlaneAttribute();
if i pass it this way, (i think the record count becomes 0)
//swimalneAttributes = GetSwimlaneAttribute as List<dynamic>;
So how to convert Dynamic to List
Thanks!
You're currently returning a sequence of anonymous type objects. That sequence can't be cast to a List<T> because it isn't a List<T>.
You could change the declaration to:
IEnumerable<dynamic> GetSwimlaneAttribute(...)
with no change to the body of the code - then to get a List<dynamic> just call it as:
List<dynamic> list = GetSwimlaneAttribute(...).ToList();
If you absolutely can't change the declaration, you could convert it outside the method:
IEnumerable<dynamic> sequence = GetSwimlaneAttribute(...);
List<dynamic> list = sequence.ToList();
Or call the extension method directly:
List<dynamic> list = Enumerable.ToList<dynamic>(GetSwimlaneAttirbute(...));
However, you should be aware that anonymous types don't cross assembly boundaries (without a bit of hackery). You should strongly consider creating a named type for this instead.
Additionally, your method body is a bit crufty - you declare a variable and assign it a null value, then immediately assign a different value, and then just return that value. The whole thing could be written as:
return swimlaneAttributeTable.Select(s => new
{
ID = s.Id,
DataType = s.AttributeDataType,
IsCriticalField = s.IsCriticalField,
});
How about this one?
List<dynamic> lstDynamic = new List<dynamic>();
lstDynamic.Add(GetSwimlaneAttribute());
and use lstDynamic.
why don't you try this way?
public List<dynamic> GetSwimlaneAttribute(List<ProjectSwimlaneAttribute> swimlaneAttributeTable, Dictionary<string, string> dic)
{
List<dynamic> swimlaneAttributes = new List<dynamic>(); // modified dynamic to List<dynamic>
swimlaneAttributes = swimlaneAttributeTable.Select(s => new
{
ID = s.Id,
DataType = s.AttributeDataType,
IsCriticalField = s.IsCriticalField,
});
return swimlaneAttributes;
}

Adding properties to an object dynamically

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"]);
}

How to create specific list type whose parameter is known at runtime and then loop through this list?

I know I can create an object whose type is known only at run time like this:
Type t = record.GetType();
var src = Activator.CreateInstance(t.BaseType);
How can I do something like List<Record>=new List<Record>() at run time?
Suppose I am getting Child Record list using Reflection like this
var ChildRecorList=src.GetType().GetProperty(propName).GetValue(src, null);
and how can then I loop through this using foreach or for loop because foreach only works for known type list. It does now work with var types. Is there way to cast Reflection value to cast at specific type whose value is known at runtime(mentioned in point 1)
You can try this to create generic type in runtime:
Type genericListType = typeof (List<>);
// if you have more than one generic argumens
// you can add your types here like typeof(MyClass),typeof(MyClass2)
Type[] genericArguments = { typeof (Record) };
// create your generic type with generic arguments
Type myGenericType = genericListType.MakeGenericType(genericArguments);
// and then you can create your instance
var recordList = Activator.CreateInstance(myGenericType);
// get your property value
recordList = src.GetType().GetProperty(propName).GetValue(src, null);
And I guess you sure your type is a List then when you creating your instance you can make a cast like this:
var recordList = (IList)Activator.CreateInstance(myGenericType);
Then you can loop through your list
foreach (var item in recordList)
{
...
}
I'm not sure If you're looking for something like this, But you can use List Inside another List or Dictionary, Also, You can store the base value with any type and get the type whenever needed, I recommend using Dictionary so that way you can name your lists:
Dictionary<String, List<Object>> Data = new Dictionary<String, List<Object>>();
Data["MyUser"] = new List<Object>();
Data["MyUser"].Add(MyDBObject);
var obj = Data["MyUser"].Find(x => x.MyKey == "MyKeyObject");
var t = obj.GetType();
var dta = (MyDBObject)obj;
foreach (var db in Data)
{
if (db.Key == "MyUser")
db.Value.Find(x => x.Name == "MyName");
}
Or If you're looking for Creating lists on runtime:
List<List<Object>> Data = new List<List<Object>>();
var mydb = Data.Count;
Data.Add(new List<Object>());
Data[mydb].Add(MyOBJ);
foreach (var db in Data)
{
if (db.Contains(MyOBJ)
return db;
}
Why don't you just use generics? You could do something like this:
public void DoSomething<T>(T type)
{
var list = new List<T>();
}
This will allow you to create a list from whichever type is passed in at runtime.

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";

Categories