c# DynamicObject class dynamic properties from loop - c#

So I've created a class that inherits DynamicObject
public class MyDynamicObject : DynamicObject{
private Dictionary<string, object> Fields = new Dictionary<string, object>();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return Fields.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
Fields[binder.Name] = value;
return true;
}
}
And call this class here
public class Program
{
public static void Main()
{
dynamic person = new MyDynamicObject();
person.firstname = "Hello";
Console.WriteLine(person.firstname);
}
}
Of course this will work. But I need to create properties from a string array like
string[] fields = new string[]{"taxid","newcol","addrs","gender"};
dynamic person = new MyDynamicObject();
foreach(var f in fields)
{
person.f = "hello";
}
So the output will be person.taxi, person.newcol, person.addrs, person.gender
Is this possible?

Expose the Fields dictionary in some way, or (better) a method that allows one to explicitly set a property by name.
Note that ExpandoObject already does this, as it can be cast to IDictionary<string, object> and then you
ExpandoObject eo = new ExpandoObject();
IDictionary<string, object> dict = eo;
dynamic d = eo;
dict["MyProperty"] = 42;
Console.WriteLine(d.MyProperty); // 42
If you can't just use ExpandoObject itself, you can copy its approach.

Okay so based on the suggestion of #Jon Hanna, I came up with a solution that fits my requirements. I created a new Add method which accept a name. Below is the updated code I used.
public class DynamicFormData : DynamicObject
{
private Dictionary<string, object> Fields = new Dictionary<string, object>();
public int Count { get { return Fields.Keys.Count; } }
public void Add(string name, string val = null)
{
if (!Fields.ContainsKey(name))
{
Fields.Add(name, val);
}
else
{
Fields[name] = val;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (Fields.ContainsKey(binder.Name))
{
result = Fields[binder.Name];
return true;
}
return base.TryGetMember(binder, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (!Fields.ContainsKey(binder.Name))
{
Fields.Add(binder.Name, value);
}
else
{
Fields[binder.Name] = value;
}
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (Fields.ContainsKey(binder.Name) &&
Fields[binder.Name] is Delegate)
{
Delegate del = Fields[binder.Name] as Delegate;
result = del.DynamicInvoke(args);
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
Then I just call it like this.
string[] fields = new string[]{"taxid","newcol","addrs","gender"};
dynamic formData = new DynamicFormData();
foreach(string field in fields)
{
formData.Add(field, null);
}

Related

Figure out which properties accessed on expando object

I use a template engine that renders templates from c# objects (nested). I would like to reflect and figure out which properties / objects are used in each template string.
An ideal way would be to build a "dummy" object representing the right shape and render this in the template. I would then inspect this object afterwards to find out which properties were accessed. This would allow me to keep this logic independant of the template library.
Any idea how i might implement this? The expando object is built dynamically like this:
var dynamicObject = new ExpandoObject() as IDictionary<string, Object>;
foreach (var property in properties) {
dynamicObject.Add(property.Key,property.Value);
}
Had some ideas along these lines:
public class DummyObject {
public DummyObject() {
Accessed = new Dictionary<string, bool>();
}
public Dictionary<string, bool> Accessed;
object MyProp {
get {
Accessed["MyProp"] = true;
return "";
}
}
}
But this custom property obviously doesn't work with the dictionary / expando object. Any ideas of a route forward here?
You can override the TryGetMember method on DynamicObject:
public sealed class LoggedPropertyAccess : DynamicObject {
public readonly HashSet<string> accessedPropertyNames = new HashSet<string>();
public override bool TryGetMember(GetMemberBinder binder, out object result) {
accessedPropertyNames.Add(binder.Name);
result = "";
return true;
}
}
and then the following will output the accessed property names
dynamic testObject = new LoggedPropertyAccess();
string firstname = testObject.FirstName;
string lastname = testObject.LastName;
foreach (var propertyName in testObject.accessedPropertyNames) {
Console.WriteLine(propertyName);
}
Console.ReadKey();
N.B. There is still an issue here -- this works only as long as the template library expects only strings from the properties. The following code will fail, because every property will return a string:
DateTime dob = testObject.DOB;
In order to resolve this, and also allow for nested objects, have TryGetMember return a new instance of LoggedPropertyAccess. Then, you can override the TryConvert method as well; where you can return different values based on the conversion to different types (complete code):
using System;
using System.Collections.Generic;
using System.Dynamic;
namespace DynamicObjectGetterOverride {
public sealed class LoggedPropertyAccess : DynamicObject {
public readonly Dictionary<string, object> __Properties = new Dictionary<string, object>();
public readonly HashSet<string> __AccessedProperties = new HashSet<string>();
public override bool TryGetMember(GetMemberBinder binder, out object result) {
if (!__Properties.TryGetValue(binder.Name, out result)) {
var ret = new LoggedPropertyAccess();
__Properties[binder.Name] = ret;
result = ret;
}
__AccessedProperties.Add(binder.Name);
return true;
}
//this allows for setting values which aren't instances of LoggedPropertyAccess
public override bool TrySetMember(SetMemberBinder binder, object value) {
__Properties[binder.Name] = value;
return true;
}
private static Dictionary<Type, Func<object>> typeActions = new Dictionary<Type, Func<object>>() {
{typeof(string), () => "dummy string" },
{typeof(int), () => 42 },
{typeof(DateTime), () => DateTime.Today }
};
public override bool TryConvert(ConvertBinder binder, out object result) {
if (typeActions.TryGetValue(binder.Type, out var action)) {
result = action();
return true;
}
return base.TryConvert(binder, out result);
}
}
}
and use as follows:
using System;
using static System.Console;
namespace DynamicObjectGetterOverride {
class Program {
static void Main(string[] args) {
dynamic testObject = new LoggedPropertyAccess();
DateTime dob = testObject.DOB;
string firstname = testObject.FirstName;
string lastname = testObject.LastName;
dynamic address = testObject.Address;
address.House = "123";
address.Street = "AnyStreet";
address.City = "Anytown";
address.State = "ST";
address.Country = "USA";
WriteLine("----- Writes the returned values from reading the properties");
WriteLine(new { firstname, lastname, dob });
WriteLine();
WriteLine("----- Writes the actual values of each property");
foreach (var kvp in testObject.__Properties) {
WriteLine($"{kvp.Key} = {kvp.Value}");
}
WriteLine();
WriteLine("----- Writes the actual values of a nested object");
foreach (var kvp in testObject.Address.__Properties) {
WriteLine($"{kvp.Key} = {kvp.Value}");
}
WriteLine();
WriteLine("----- Writes the names of the accessed properties");
foreach (var propertyName in testObject.__AccessedProperties) {
WriteLine(propertyName);
}
ReadKey();
}
}
}

Change System.Dynamic.ExpandoObject default behavior

I've a dynamic object created using System.Dynamic.ExpandoObject(), now in some cases some properties could not exists, and if try to access to those in this way
myObject.undefinedProperties;
the default behavior of the object is to throw the exception
'System.Dynamic.ExpandoObject' does not contain a definition for 'undefinedProperties'
Is possible to change this behavior and return in that case the null value?
If you could replace ExpandoObject with DynamicObject, you could write own class that meets your requirements:
public class MyExpandoReplacement : DynamicObject
{
private Dictionary<string, object> _properties = new Dictionary<string, object>();
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!_properties.ContainsKey(binder.Name))
{
result = GetDefault(binder.ReturnType);
return true;
}
return _properties.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
this._properties[binder.Name] = value;
return true;
}
private static object GetDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
}
Usage:
dynamic a = new MyExpandoReplacement();
a.Sample = "a";
string samp = a.Sample; // "a"
string samp2 = a.Sample2; // null
ExpandoObject inherits IDictionary <string, object> so you can check if the object has "undefinedProperties" like this
if (((IDictionary<string, object>)myObject).ContainsKey("undefinedProperties"))
{
// Do something
}
You can test the existence of property in the ExpandoObject, see here Detect property in ExpandoObject

How do you use CsvHelper to write a class derived from DynamicObject?

I was hoping to use a dynamically typed object to write to a CSV file.
I'm receiving a 'CsvHelper.CsvWriterException' within the CsvWriter.WriteObject method with this message: "No properties are mapped for type 'WpmExport.DynamicEntry'."
Here is the class that I'm trying use :
public class DynamicEntry : DynamicObject
{
private Dictionary<string, object> dictionary = new Dictionary<string, object>();
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
string name = binder.Name.ToLower();
return dictionary.TryGetValue(name, out result);
}
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
dictionary[binder.Name.ToLower()] = value;
return true;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return dictionary.Keys.AsEnumerable();
}
}
Anyone with any ideas or working examples? The documentation at http://joshclose.github.io/CsvHelper/ hints that it is possible but doesn't provide any guidance.
TIA
The functionality does not exist yet. You can write dynamic but not DynamicObject. You can view a thread on the subject here. https://github.com/JoshClose/CsvHelper/issues/187
When the functionality get implemented, I'll update the answer with the version it's in.
Update
This functionality will be available in 3.0. You can currently try out the 3.0-beta from NuGet.
Because I cannot wait for Version 3.0 (and CsvHelper.Excel to support it), I have found a interim-solution.
Got the class to export:
public partial class EntryReportInventory
{
public Guid DeviceId { get; set; }
[ReportProperty]
public string DeviceName { get; set; }
public Dictionary<string, object> InventoryValues { get; set; }
public EntryReportInventory(Device device, Dictionary<string, object> inventoryValues)
{
this.DeviceId = device.Id;
this.DeviceName = device.Name;
this.InventoryValues = inventoryValues;
}
}
Created Mapper:
Type genericClass = typeof(DefaultCsvClassMap<>);
Type constructedClass = genericClass.MakeGenericType(typeof(EntryReportInventory));
return (CsvClassMap)Activator.CreateInstance(constructedClass);
And now the magic. I iterate all properties.
foreach (PropertyInfo property in mapping)
{
...
if (isInventoryReportBaseType && typeof(Dictionary<string, object>).IsAssignableFrom(property.PropertyType))
{
var dataSource = (ReportInventoryBase)Activator.CreateInstance(entityType, dbContext);
foreach (var item in dataSource.ColumnNameAndText)
{
var columnName = item.Key;
var newMap = new CsvPropertyMap(property);
newMap.Name(columnName);
newMap.TypeConverter(new InventoryEntryListSpecifiedTypeConverter(item.Key));
customMap.PropertyMaps.Add(newMap);
}
...
}
And my converter is:
public class InventoryEntryListSpecifiedTypeConverter : CsvHelper.TypeConversion.ITypeConverter
{
private string indexKey;
public InventoryEntryListSpecifiedTypeConverter(string indexKey)
{
this.indexKey = indexKey;
}
public bool CanConvertFrom(Type type)
{
return true;
}
public bool CanConvertTo(Type type)
{
return true;
}
public object ConvertFromString(TypeConverterOptions options, string text)
{
throw new NotImplementedException();
}
public string ConvertToString(TypeConverterOptions options, object value)
{
var myValue = value as Dictionary<string, object>;
if (value == null || myValue.Count == 0) return null;
return myValue[indexKey] + "";
}
}
Don't know why, but it works to pass the same property several times.
That's it :)
You only have to have a list before (here: dataSource.ColumnNameAndText, filled from an external source) to identify the columns/values.

C# dynamic classes

I'm talking about something similar to dynamic. This didn't answer my question, hence this question. I want to have a class that I can add properties to at runtime. It needs to be inherited from the type object.
I've seen inheriting from DynamicObject, but it didn't state how to add properties at run-time. Could some light be shed on this for me pls?
I have a class like this:
public class SomeModel : DynamicObject {
public string SomeMandatoryProperty {get; set;}
}
I'd like to add all properties from another class to this class at runtime. So eg.
SomeModel m = new SomeModel();
m = someOtherClass;
string hi = m.otherClassProp; //Property from other class is added.
string mandatory = m.SomeMandatoryProperty; //From the mandatory property set previously.
I think you are looking for ExpandoObject:
The ExpandoObject class enables you to
add and delete members of its
instances at run time and also to set
and get values of these members. This
class supports dynamic binding, which
enables you to use standard syntax
like sampleObject.sampleMember instead
of more complex syntax like
sampleObject.GetAttribute("sampleMember").
dynamic manager;
manager = new ExpandoObject();
manager.Name = "Allison Brown";
manager.Age = 42;
manager.TeamSize = 10;
You should be able to make use of ExpandoObject instead. An ExpandoObject can have members added or removed at runtime and has very nice support if you want to convert to XML etc.
From MSDN Documentation:
dynamic employee, manager;
employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;
manager = new ExpandoObject();
manager.Name = "Allison Brown";
manager.Age = 42;
manager.TeamSize = 10;
You'd want to use the ExpandoObject as you can dynamically add properties as needed. There isn't however a direct way to populate an instance with the values from another object easily. You'll have to add it manually using reflection.
Do you want to write a wrapper object where you could add properties to while still accessing the inner? You may want to consider it that way you don't have to manage two copies of values between two different object instances. I wrote a test class to wrap string objects to demonstrate how you can do this (similar to how the ExpandoObject works). It should give you an idea on how you can do this for your types.
class DynamicString : DynamicObject
{
static readonly Type strType = typeof(string);
private string instance;
private Dictionary<string, object> dynProperties;
public DynamicString(string instance)
{
this.instance = instance;
dynProperties = new Dictionary<string, object>();
}
public string GetPrefixString(string prefix)
{
return String.Concat(prefix, instance);
}
public string GetSuffixString(string suffix)
{
return String.Concat(instance, suffix);
}
public override string ToString()
{
return instance;
}
public override bool TryConvert(ConvertBinder binder, out object result)
{
if (binder.Type != typeof(string))
return base.TryConvert(binder, out result);
result = instance;
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
var method = strType.GetMethod(binder.Name, args.Select(a => a.GetType()).ToArray());
if (method == null)
{
result = null;
return false;
}
result = method.Invoke(instance, args);
return true;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var members = strType.GetMember(binder.Name);
if (members.Length > 0)
{
var member = members.Single();
switch (member.MemberType)
{
case MemberTypes.Property:
result = ((PropertyInfo)member).GetValue(instance, null);
return true;
break;
case MemberTypes.Field:
result = ((FieldInfo)member).GetValue(instance);
return true;
break;
}
}
return dynProperties.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
var ret = base.TrySetMember(binder, value);
if (ret) return true;
dynProperties[binder.Name] = value;
return true;
}
}
If you want to be adventurous, you can define your own meta objects to handle the bindings. You could end up with reusable meta objects for different types and simplify your code immensely. I've been playing with this for a while and have this so far. It doesn't handle dynamically adding properties yet. I won't be working on this any further but I'll just leave it here for reference.
class DynamicString : DynamicObject
{
class DynamicStringMetaObject : DynamicMetaObject
{
public DynamicStringMetaObject(Expression parameter, object value)
: base(parameter, BindingRestrictions.Empty, value)
{
}
public override DynamicMetaObject BindConvert(ConvertBinder binder)
{
if (binder.Type == typeof(string))
{
var valueType = Value.GetType();
return new DynamicMetaObject(
Expression.MakeMemberAccess(
Expression.Convert(Expression, valueType),
valueType.GetProperty("Instance")),
BindingRestrictions.GetTypeRestriction(Expression, valueType));
}
return base.BindConvert(binder);
}
public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
System.Diagnostics.Trace.WriteLine(String.Format("BindGetMember: {0}", binder.Name));
var valueType = Value.GetType();
var self = Expression.Convert(Expression, valueType);
var valueMembers = valueType.GetMember(binder.Name);
if (valueMembers.Length > 0)
{
return BindGetMember(self, valueMembers.Single());
}
var members = typeof(string).GetMember(binder.Name);
if (members.Length > 0)
{
var instance =
Expression.MakeMemberAccess(
self,
valueType.GetProperty("Instance"));
return BindGetMember(instance, members.Single());
}
return base.BindGetMember(binder);
}
private DynamicMetaObject BindGetMember(Expression instance, MemberInfo member)
{
return new DynamicMetaObject(
Expression.Convert(
Expression.MakeMemberAccess(instance, member),
typeof(object)),
BindingRestrictions.GetTypeRestriction(Expression, Value.GetType())
);
}
}
public string Instance { get; private set; }
public DynamicString(string instance)
{
Instance = instance;
}
public override DynamicMetaObject GetMetaObject(Expression parameter)
{
return new DynamicStringMetaObject(parameter, this);
}
public override string ToString()
{
return Instance;
}
public string GetPrefixString(string prefix)
{
return String.Concat(prefix, Instance);
}
public string GetSuffixString(string suffix)
{
return String.Concat(Instance, suffix);
}
}

DynamicObject. How execute function through TryInvoke?

I want to execute static functions through DynamicObject, but I don't know how execute saveOperation without specifying the class name typeof(Test1) or typeof(Test2) . How relise this better?
For example
class DynObj : DynamicObject
{
GetMemberBinder saveOperation;
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
saveOperation = binder;
result = this;
return true;
}
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
Type myType = typeof(Test1 or Test2 or ....);
result = myType.GetMethod(saveOperation.Name).Invoke(null, args);
return true;
}
}
class Program
{
static void Main(string[] args)
{
dynamic d1 = new DynObj();
d1.func1(3,6);
d1.func2(3,6);
}
}
class Test1
{
public static void func1(int a, int b){...}
}
class Test2
{
public static void func2(int a, int b){ ...}
}
Second way defines static function with attribute ( offered by Carnifex )
class Test3
{
[DynFuncMemberAttribute]
public static void func3(int a, int b){...}
}
and get type
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
Type myType = null;
foreach (Type types in Assembly.GetExecutingAssembly().GetTypes())
{
foreach (MethodInfo mi in types.GetMethods())
{
foreach (CustomAttributeData cad in mi.CustomAttributes)
{
if (cad.AttributeType == typeof(DynFuncMemberAttribute))
{
myType = types;
break;
}
}
}
}
result = (myType != null)? myType.GetMethod(saveOperation.Name).Invoke(null, args): null;
return myType != null;
}
You could use some Attributes and set eg. [DynFuncMemberAttribute] to the class or to the method it self.
Then inside TryInvoke (or constructor) get all types/methods marked with this attribute, build some map/cache and voila :)
Edit: this is example of use this attribute. Remember that BuildCache() will throw exception if two method with the same name will be found.
[AttributeUsage(AttributeTargets.Method)]
class DynFuncMemberAttribute : Attribute
{
}
class DynObj : DynamicObject
{
Dictionary<string, MethodInfo> cache;
GetMemberBinder saveOperation;
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
saveOperation = binder;
result = this;
return true;
}
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
if (cache == null)
cache = BuildCache();
MethodInfo mi;
if (cache.TryGetValue(saveOperation.Name, out mi))
{
result = mi.Invoke(null, args);
return true;
}
result = null;
return false;
}
private Dictionary<string, MethodInfo> BuildCache()
{
return Assembly.GetEntryAssembly()
.GetTypes()
.SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.Static))
.Where(mi => mi.GetCustomAttribute<DynFuncMemberAttribute>() != null)
.ToDictionary(mi => mi.Name);
}
}
class Program
{
static void Main(string[] args)
{
dynamic d1 = new DynObj();
d1.func1(3, 6);
d1.func2(3, 6);
}
}
class Test1
{
[DynFuncMember]
public static void func1(int a, int b)
{
Console.WriteLine("func1");
}
}
class Test2
{
[DynFuncMember]
public static void func2(int a, int b)
{
Console.WriteLine("func2");
}
}

Categories

Resources