1

Actually I try to design some kind of e-shop. I am interested in creation of many kinds of products from admin panel (during runtime).

For example I have basic Product class

public class Product
{ 
     public double Price {get;set;}
}

And I want to create some TV class which has ScreenDiagonal

public class TV : Product
{ 
     public int ScreenDiagonal {get;set;}
}

But I need to create such classes in runtime, and set properties for it. What are the best practices of doing this? Reflection or some other ideas?

0

1 Answer 1

2

use CodeDom, like this:

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 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;
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.