So I know how to add properties to an ExpandoObject as a dictionary, but I'm not sure how to add Func's:
For example:
var foo = (IDictionary<string, object>)new ExpandoObject();
foo.Add("Bar", "SomeValue");
is fine. However I'm not sure how I'd then go on to add
foo.Add("FooFunc", (someString) =>
{
return someString;
});
as its not an object. Any help appeciated, thanks in advance
Funcs are objects but you may have to be explicit about adding one:
foo.Add("FooFunc", new Func<string, string>(someString => {
//...
return someString;
});
Related
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));
}
This question may be a bit of a long shot however please read on:
I have a set of stored procedures that are run and all of their data is stored in dictionarys.
For example getEventTypes is stored in the eventTypeResult dictionary and the getEventCats is stored in the dictonary eventCatResult.
Is it possible to run the following line dynamicly:
Dictionary<string, eventTypeResult> resultsList = (Dictionary<string, eventTypeResult>)resultsObject;
Where is could look something like this:
var resultType = eventTypeResult;
Dictionary<string, resultType > resultsList = (Dictionary<string, resultType >)resultsObject;
Thanks for any help.
EDIT: Attempt 2 at explaining why it needs to be dynamic
Currently my code looks like this:
if (resultType == "eventTypeResult")
{
Dictionary<string, eventTypeResult> resultsList = (Dictionary<string, eventTypeResult>)resultsObject;
}
else if (resultType == "eventDateResult")
{
Dictionary<string, eventDateResult> resultsList = (Dictionary<string, eventDateResult>)resultsObject;
}
else if
else if
else if
ect
ect
Could it not just be like this:
var resultType = eventTypeResult;
Dictionary<string, resultType > resultsList = (Dictionary<string, resultType >)resultsObject;
"Dynamic" is probably not the best approach in a strongly typed language...
What you are trying to create is an arbitrary name value collection, like the various cache types in the .Net Framework. you need to relay on a common type, that can hold all of your result types.
if you have such a type something like resultType that all the rest of the result Types inherit from, then use it: Dictionary<string, resultType > . you can put what ever you want in the value as long as it is a sub class of resultType. If you dont then use Dictionary<string, object >
if you need to cast between Dictioneries you can always (the object acutal type must match):
var thing = new Dictionery<int> ()
var objectThing = thing.Cast<Dictionery<object>();
if you want dynamic you can:
Dictionary<string, resultType > dict;
dyamic value = dict["something"];
value.WhaterverYouWant() // if this method doesnt exist this will compile but crash at runtime...
I have a dynamic object that looks like this,
{
"2" : "foo",
"5" : "bar",
"8" : "foobar"
}
How can I convert this to a dictionary?
You can fill the dictionary using reflection:
public Dictionary<String, Object> Dyn2Dict(dynamic dynObj)
{
var dictionary = new Dictionary<string, object>();
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(dynObj))
{
object obj = propertyDescriptor.GetValue(dynObj);
dictionary.Add(propertyDescriptor.Name, obj);
}
return dictionary;
}
You can use a RouteValueDictionary to convert a C# object to a dictionary. See: RouteValueDictionary Class - MSDN. It converts object properties to key-value pairs.
Use it like this:
var toBeConverted = new {
foo = 2,
bar = 5,
foobar = 8
};
var result = new RouteValueDictionary(toBeConverted);
If the dynamic value in question was created via deserialization from Json.Net as you mentioned in your comments, then it should be a JObject. It turns out that JObject already implements IDictionary<string, JToken>, so you can use it as a dictionary without any conversion, as shown below:
string json =
#"{ ""blah"" : { ""2"" : ""foo"", ""5"" : ""bar"", ""8"" : ""foobar"" } }";
var dict = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(json);
dynamic dyn = dict["blah"];
Console.WriteLine(dyn.GetType().FullName); // Newtonsoft.Json.Linq.JObject
Console.WriteLine(dyn["2"].ToString()); // foo
If you would rather have a Dictionary<string, string> instead, you can convert it like this:
Dictionary<string, string> newDict =
((IEnumerable<KeyValuePair<string, JToken>>)dyn)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToString());
You can use Json.Net to deserialize it to dictionary.
string json = dynamicObject.ToString(); // suppose `dynamicObject` is your input
Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
Very similar to ema answer, but with a one-liner using LINQ magic:
Dictionary<string, object> myDict = sourceObject.GetType().GetProperties().ToDictionary(prop => prop.Name, prop => prop.GetValue(sourceObject, null));
Another way is using System.Web.Helpers.Json included in .NET 4.5.
Json.Encode(object) and Json.Decode. Like:
Json.Decode<Generic.Dictionary<string, string>>(value);
MSDN: https://msdn.microsoft.com/en-us/library/gg547931(v=vs.111).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
Regards,
MarianoC.
You can do it with jsonSerializer. And it requires System.Net.Extensions reference. Here is a sample code.
var jss = new JavaScriptSerializer();
var dict = jss.Deserialize<Dictionary<string,string>>(jsonText);
var place = dict["8"]; // "foobar"
If you use the dynamic implementation here:
https://github.com/b9chris/GracefulDynamicDictionary
You can get the Dictionary right from the implementation. One advantage to using the above implementation (written for an answer to another SO question), is you can shift easily between the specific implementation and dynamic, like so:
dynamic headers = new DDict();
headers.Authorization = token;
if (doesNeedSiteId)
headers.SiteId = siteId;
await post(headers);
}
protected async Task post(DDict headers)
{
var dict = headers.GetDictionary(); // Dictionary<string, object>
In the above, the headers collection is conveniently created as a dynamic, but, the underlying specific implementation is DDict, and the post() method accepts it even though you've declared it as dynamic and used its features.
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");
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;";