Is there anything built into .NET 4.5 that will generate a string C# POCO from a dynamic
with all auto-implemented properties?
If not, is there anything built into .NET that will give you (something like a) List<KeyValuePair<string, Type>>
so that we can generate a POCO according to the pseudo-code:
foreach (var kvp in list)
{
builder.AppendFormat("public {0} {1} {{ get; set; }}", kvp.Value, kvp.Key);
}
Finally, are there any well-known libraries that can assist with this sort of very basic code generation?
You can use compileassemblyfromsource
to compile your string,
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
var cp = new CompilerParameters()
{
GenerateExecutable = false,
GenerateInMemory = true
};
cp.ReferencedAssemblies.Add("mscorlib.dll");
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Core.dll");
// The string can contain any valid c# code
// A valid class need to be created with its own properties.
var s = "public class POCOClass{ public int ID {get {return 1;}} }";
// "results" will usually contain very detailed error messages
var results = csc.CompileAssemblyFromSource(cp, s);
var type = results.CompiledAssembly.GetType("POCOClass");
var obj = (dynamic)Activator.CreateInstance(type);
var output = obj.ID;
// or
var output_ = obj.GetType().GetProperty("ID").GetValue(obj, null);
You need to define a class for your properties such as called POCOClass.
EDIT:
public static T CopyObjectFromExpando<T>(this object s) where T : class
{
var source = (ExpandoObject)s;
// Might as well take care of null references early.
if (source == null)
{
throw new ArgumentNullException("s");
}
var propertyMap = typeof(T).GetProperties().ToDictionary(p => p.Name.ToLowerInvariant(), p => p);
var destination = Activator.CreateInstance<T>();
// By iterating the KeyValuePair<string, object> of
// source we can avoid manually searching the keys of
// source as we see in your original code.
foreach (var kv in source)
{
PropertyInfo p;
if (propertyMap.TryGetValue(kv.Key.ToLowerInvariant(), out p))
{
var propType = p.PropertyType;
if (kv.Value == null)
{
if (!propType.IsNullable() && propType != typeof(string))
{
// Throw if type is a value type
// but not Nullable<>
throw new ArgumentException("not nullable");
}
}
else if (propType.IsEnum)
{
var enumvalue = Enum.ToObject(propType, kv.Value);
p.SetValue(destination, enumvalue, null);
continue;
}
else if (propType == typeof(bool) && kv.Value.GetType() != typeof(bool))
{
var boolvalue = Convert.ToBoolean(kv.Value);
p.SetValue(destination, boolvalue, null);
continue;
}
else if (propType.IsNullable())
{
var nullType = Nullable.GetUnderlyingType(propType);
var value = Convert.ChangeType(kv.Value, nullType);
p.SetValue(destination, value, null);
continue;
}
else if (kv.Value.GetType() != propType)
{
// You could make this a bit less strict
// but I don't recommend it.
throw new ArgumentException("type mismatch");
}
p.SetValue(destination, kv.Value, null);
}
}
return destination;
}