Search code examples
c#dynamicreflectioncode-generationpoco

Generate POCO from dynamic in C#


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?


Solution

  • You can use compileassemblyfromsource to compile your string,

    http://msdn.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider.compileassemblyfromsource(v=vs.110).aspx

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