Search code examples
c#dynamicexpandoobject

How can I use a dynamic to find out when a property is used?


I would like to find out which of the properties in a source input object, a method has used. After executing the method I need to store in a database which of the properties was used.

The input could be any class with simple types, like this:

public class MyData : IMyData
{
    public string A { get; set; }
    public int B { get; set; }
    public decimal C { get; set; }
}

I thought it could be done using an interface as input to the method, so I can replace the original object with a more advanced object, which stores usage of properties

public interface IMyData
{
    string A { get; }
    int B { get; }
    decimal C { get; }
}

I can then

  1. Create a dynamic object with the same properties
  2. Use ImpromptuInterface to simulate the dynamic object implements my interface
  3. Call my method with this dynamic interface

    private static void Main()
    {
        var data = new MyData { A = "Test", B = 3, C = new decimal(1.2) };
    
        IDictionary<string, object> replacementObject = new ExpandoObject();
        replacementObject.Add("FieldsUsed", new List<string>());
        foreach (var property in data.GetType().GetProperties())
            replacementObject.Add(property.Name, property.GetValue(data));
    
        var replacementInterface = replacementObject.ActLike<IMyData>();
    
        DoStuff(replacementInterface);
        Console.WriteLine($"The method used these fields {string.Join(", ", (List<string>)replacementObject["FieldsUsed"])}");
    }
    
    private static void DoStuff(IMyData source)
    {
        Console.WriteLine($"A is {source.A}");
        if (source.B > 5)
            Console.WriteLine($"C is {source.C}");
    }
    

In the above example I would like to store that fields A and B have been used.

Only I am stuck at how I should store when a property is used by my DoStuff method.


Solution

  • You can write a wrapper like this:

    public class ClassWrapper<T>: DynamicObject where T:class
    {
        private readonly T _obj;
        private readonly List<string> _fieldsUsed=new List<string>();
        public ClassWrapper(T obj)
        {
            _obj = obj;
        }
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            PropertyInfo propertyInfo = _obj.GetType().GetProperty(binder.Name);
            _fieldsUsed.Add(binder.Name);
            result = propertyInfo.GetValue(_obj);
            return true;
        }
        public List<string> GetFieldsUsed() => _fieldsUsed;
    
        public T GetWrapper()
        {
            return this.ActLike<T>();
        }
    }
    

    and use it like

     var data = new MyData { A = "Test", B = 3, C = new decimal(1.2) };
     var mc=new ClassWrapper<IMyData>(data);
    
     IMyData wrapped = mc.GetWrapper();
    
     DoStuff(wrapped);
     Console.WriteLine($"The method used these fields {string.Join(", ", (List<string>)mc.GetFieldsUsed())}");