21

Is it possible to do the following in C# (or in any other language)?

  1. I am fetching data from a database. At run time I can compute the number of columns and data types of the columns fetched.

  2. Next I want to "generate" a class with these data types as fields. I also want to store all the records that I fetched in a collection.

The problem is that I want to do both step 1 and 2 at runtime

Is this possible? I am using C# currently but I can shift to something else if i need to.

8
  • 4
    Do you really need this? You can surely (A) generate a custom class as others pointed out, but you also need to (B) know how to use it at run time. The part (B) seems like also a lot of work to me. What is wrong with keeping data inside the DataSet object or some sort of collection such as dictionary? What are you trying to do?
    – Job
    Jul 16, 2011 at 13:39
  • Make sure you have a look at the work Rob Conery did with dynamic in Massive: blog.wekeroad.com/helpy-stuff/and-i-shall-call-it-massive Jul 16, 2011 at 18:15
  • 1
    Python allows for dynamic class declaration, and in fact it's common. There was a tutorial by David Mertz from around 2001 (I searched for it but couldn't find the exact link). It is straightforward.
    – smci
    Jul 17, 2011 at 0:44
  • @RobertHarvey The link you shared is dead. Do you know where can I find it?
    – Jose Luis
    Jun 15, 2015 at 9:01
  • 1
    Although technically possible, I would have to question the value of doing so. The point of strong typing and classes is so that at compile time you are able to make use of the class information to check for syntax errors (newer generation of dynamic JITers can optimize without this). Clearly by trying to use dynamic typing in strongly typed environment would mean that you lose all advantages of either paradigms...
    – ArTs
    Jan 20, 2016 at 3:06

2 Answers 2

28

Use CodeDom. Here's something to get started

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.CodeDom;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            string className = "BlogPost";

            var props = new Dictionary<string, Type>() {
                { "Title", typeof(string) },
                { "Text", typeof(string) },
                { "Tags", typeof(string[]) }
            };

            createType(className, props);
        }

        static void createType(string name, IDictionary<string, Type> props)
        {
            var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
            var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll"}, "Test.Dynamic.dll", false);
            parameters.GenerateExecutable = false;

            var compileUnit = new CodeCompileUnit();
            var ns = new CodeNamespace("Test.Dynamic");
            compileUnit.Namespaces.Add(ns);
            ns.Imports.Add(new CodeNamespaceImport("System"));

            var classType = new CodeTypeDeclaration(name);
            classType.Attributes = MemberAttributes.Public;
            ns.Types.Add(classType);

            foreach (var prop in props)
            {
                var fieldName = "_" + prop.Key;
                var field = new CodeMemberField(prop.Value, fieldName);
                classType.Members.Add(field);

                var property = new CodeMemberProperty();
                property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
                property.Type = new CodeTypeReference(prop.Value);
                property.Name = prop.Key;
                property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
                property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodePropertySetValueReferenceExpression()));
                classType.Members.Add(property);
            }

            var results = csc.CompileAssemblyFromDom(parameters,compileUnit);
            results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
        }
    }
}

It creates an assembly 'Test.Dynamic.dll' with this class in it

namespace Test.Dynamic
{
    public class BlogPost
    {
        private string _Title;
        private string _Text;
        private string[] _Tags;

        public string Title
        {
            get
            {
                return this._Title;
            }
            set
            {
                this._Title = value;
            }
        }
        public string Text
        {
            get
            {
                return this._Text;
            }
            set
            {
                this._Text = value;
            }
        }
        public string[] Tags
        {
            get
            {
                return this._Tags;
            }
            set
            {
                this._Tags = value;
            }
        }
    }
}

You could also use dynamic features of C#

DynamicEntity class, no need to create anything at runtime

public class DynamicEntity : DynamicObject
{
    private IDictionary<string, object> _values;

    public DynamicEntity(IDictionary<string, object> values)
    {
        _values = values;
    }
    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return _values.Keys;
    }
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (_values.ContainsKey(binder.Name))
        {
            result = _values[binder.Name];
            return true;
        }
        result = null;
        return false;
    }
}

And use it like this

var values = new Dictionary<string, object>();
values.Add("Title", "Hello World!");
values.Add("Text", "My first post");
values.Add("Tags", new[] { "hello", "world" });

var post = new DynamicEntity(values);

dynamic dynPost = post;
var text = dynPost.Text;
0
6

Yes, you can use reflection emit to do this. Manning have what looks to be an excellent book coming out on this: Metaprogramming in .NET.

BTW: why not just use a list containing dictionaries or similar to store each record with the field names as keys?

1

Not the answer you're looking for? Browse other questions tagged or ask your own question.