I have a bunch of entity classes with just a whole bunch of properties
in it. I have to count each time one of these properties is read and
written to. I was hoping I could implement a pattern that would allow
me to do that easily so that I didn't have to write custom setters and
getters for each property (I'm using automatic properties for
convenience). Does anyone have any idea how I might be able to do this
easily?
The more I think about it the more it seems unlikely there is any
other solution. My mind keeps veering toward events when I try to
decide how I might easily do this but that's definitely not it. This
post is just hoping there is something I haven't thought of.
My suggestion would be to:
- make the properties virtual
- dynamicly generate a wrapper class that does the book keeping
A simple POC is attached below.
Arne
======================
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Microsoft.CSharp;
namespace E
{
public class A
{
public virtual int Iv { get; set; }
public virtual string Sv { get; set; }
}
public class B
{
public virtual int Iv { get; set; }
public virtual double Xv { get; set; }
}
public interface IWrapper
{
int CountGets(string prop);
int CountSets(string prop);
}
public class WrapperGen
{
private Dictionary<Type, Type> cache = new Dictionary<Type,
Type>();
private const string nstemp = @"using System;
using E;
namespace WrapperGen
{{
{0}
}}
";
private const string clztemp = @" public class {0} : {1},
IWrapper
{{
{2}
}}
";
private const string fldtemp = @" private {0} del;
";
private const string contemp = @" public {0}({1} del)
{{
this.del = del;
}}
";
private const string proptemp = @" private int cGet{1} = 0;
private int cSet{1} = 0;
public override {0} {1}
{{
get
{{
cGet{1}++;
return del.{1};
}}
set
{{
cSet{1}++;
del.{1} = value;
}}
}}
";
private const string methgettemp = @" public int
CountGets(string prop)
{{
switch(prop)
{{
{0}
default:
throw new ArgumentException(prop + "" is not a
valid property"");
}}
}}
";
private const string lblgettemp = @" case ""{0}"":
return cGet{0};
break;
";
private const string methsettemp = @" public int
CountSets(string prop)
{{
switch(prop)
{{
{0}
default:
throw new ArgumentException(prop + "" is not a
valid property"");
}}
}}
";
private const string lblsettemp = @" case ""{0}"":
return cSet{0};
break;
";
private Type Create(Type t)
{
StringBuilder sb = new StringBuilder();
StringBuilder sbget = new StringBuilder();
StringBuilder sbset = new StringBuilder();
string clznam = t.Name + "Wrapper";
sb.Append(String.Format(fldtemp, t.Name));
sb.Append(String.Format(contemp, clznam, t.Name));
foreach(PropertyInfo p in t.GetProperties())
{
sb.Append(String.Format(proptemp, p.PropertyType.Name,
p.Name));
sbget.Append(String.Format(lblgettemp, p.Name));
sbset.Append(String.Format(lblsettemp, p.Name));
}
sb.Append(String.Format(methgettemp, sbget.ToString()));
sb.Append(String.Format(methsettemp, sbset.ToString()));
string src = String.Format(nstemp, String.Format(clztemp,
clznam, t.Name, sb.ToString()));
CodeDomProvider comp = new CSharpCodeProvider();
CompilerParameters param = new CompilerParameters();
param.GenerateInMemory = true;
param.ReferencedAssemblies.Add("System.dll");
param.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
CompilerResults res = comp.CompileAssemblyFromSource(param,
src);
if(res.Errors.Count > 0)
{
Console.WriteLine(src);
foreach(CompilerError ce in res.Errors)
{
Console.WriteLine(ce);
}
}
Assembly asm = res.CompiledAssembly;
return asm.GetType("WrapperGen." + clznam);
}
public T Get<T>(T o) where T : class
{
if(!cache.ContainsKey(o.GetType()))
{
cache.Add(o.GetType(), Create(o.GetType()));
}
return (T)cache[o.GetType()].GetConstructor(new Type[] {
o.GetType() }).Invoke(new object[] { o });
}
}
public class Program
{
public static void Main(string[] args)
{
WrapperGen wg = new WrapperGen();
A a = wg.Get(new A());
a.Iv = a.Iv + 1;
a.Sv = "X";
Console.WriteLine(((IWrapper)a).CountGets("Iv") + " " +
((IWrapper)a).CountSets("Iv"));
Console.WriteLine(((IWrapper)a).CountGets("Sv") + " " +
((IWrapper)a).CountSets("Sv"));
B b = wg.Get(new B());
b.Iv = 77;
for(int i = 0; i < 10; i++)
{
int v = b.Iv;
}
Console.WriteLine(((IWrapper)b).CountGets("Iv") + " " +
((IWrapper)b).CountSets("Iv"));
Console.WriteLine(((IWrapper)b).CountGets("Xv") + " " +
((IWrapper)b).CountSets("Xv"));
}
}
}