C# Dynamic TValue in Dictionary - c#

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...

Related

How to declare a type of (string, IDictionary<string, object>)

I am fixing a bug on a legacy system. It has a function with return type (string, IDictionary<string, object>). I cannot change the method signature. I want to declare a variable of method return type. I tried this but its giving me an error.
var sqlQuery = new (string, IDictionary<string, object>)
This is a value tuple type (available in C# 7.0 and later) and you can either use target-typed new expression (since C# 9) to fill it with default values:
(string, IDictionary<string, object>) sqlQuery = new();
or using default:
(string, IDictionary<string, object>) sqlQuery = default;
or provide values of needed types:
var sqlQuery = (someString, someDictionary);
// or
var sqlQuery = ((string)null, (IDictionary<string, object>)null);
Or just use result of the method returning the tuple:
var sql = MethodReturningTuple();
Tuple can have multiple data types and that is what the function returns in your case.
you can create a var
var sqlQuery = method();
while debugging, you can verify that data is filled properly into the tuple.
or you can create specific type per method return
(string, IDictionary<string, object>) sqlQuery = method();
It is better to use var as it can change with method signature. Having said that, every pro has cons, code can fail if the type is changed. C# is type safe and (hopefully) compile will detect the type change.

Create Tuple Using List of datatype of string in c#

I need to create Tuple from list of datatype in string, but did not get any solution.
Here is example of what I want to do.
string[] dataType = {"int", "float", "datetime"};
//I want to create list like this but dynamically datatype come from db.
//List<Tuple<int, string, string>> list = new List<Tuple<int, string, string>>();
List<Tuple<dataType[0],dataType[1], dataType[2]>> list = new List<Tuple<dataType[0],dataType[1], dataType[2]>>();
//Here datatype value is in string so I want to convert string to actual datatype.
Or if any alternative solution for this please guide me.
This is to extend my comments under question, and show you what I meant there.
It is not hard to create a list of Tuple dynamically from list of types in string format. For example, use reflection:
private Type InferType(string typeName)
{
switch (typeName.ToLowerInvariant())
{
case "int":
return typeof(int);
case "float":
return typeof(float);
default:
return Type.GetType($"System.{typeName}", true, true);
}
}
private object CreateListOfTupleFromTypes(string[] types)
{
var elementTypes = types.Select(InferType).ToArray();
// Get Tuple<,,>
var tupleDef = typeof(Tuple)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.First(mi => mi.Name == "Create"
&& mi.GetParameters().Count() == elementTypes.Length)
.ReturnType
.GetGenericTypeDefinition();
// Get Tuple<int, float, DateTime>
var tupleType = tupleDef.MakeGenericType(elementTypes);
// Get List<Tuple<int, float, DateTime>>
var listType = typeof(List<>).MakeGenericType(tupleType);
// Create list of tuple.
var list = Activator.CreateInstance(listType);
return list;
}
The problem is because the list is created using types only known at runtime, in your code, you can never use the list as strong typed list. i.e.List<Tuple<int, float, DateTime>>.
Of course, you could make life easier when the list is used in your code by using ITuple:
var list = new List<ITuple>();
list.Add(new Tuple<int, float, DateTime>(...);
int value1 = (int)list[0][0];
float value1 = (float)list[0][1];
DateTime value1 = (DateTime)list[0][2];
However, if you do that, then there is no point to use Tuple. You only need List<object[]>.
So, this comes back to my question, what is the list of tuple for in your code?
You could use dynamic here for your types. For example, when you read the three values from your database you could assign them without worrying about the types like so:
dynamic a = 10;
dynamic b = "Some String";
dynamic c = new DateTime(2020,4,9);
var test = new System.Tuple<dynamic,dynamic,dynamic>(a, b, c);
It would depend on what you want to do with the values later on.
To create a list of those tuples use:
var list = new List<System.Tuple<dynamic,dynamic,dynamic>>();
You can get the type of the variable, for instance a with
a.GetType().ToString();
I am not sure if you can set a variable type based on a string value, though (the second part of your question).

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

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

In C#, how do I remove a property from an ExpandoObject?

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