Creating objects at runtime

For the past few days, I’ve been working with a fairly large dataset (2.87GB) that is in a collection of 66 different tabular delimited files.   This in and of itself isn’t bad, but the problem is that I was wanting to be able to easily place it into various formats and have an easy way of working with it.  This is where dynamically creating objects comes in handy.

Normally in this situation, I would go through and create POCO’s to hold the data while I worked with it, but with this number of files and with each file being its own collection of objects that would prove fruitless.  Coincidentally, the day before I started on this I also started reading Metaprogramming in .NET, this provided me with a better path of handling this issue.   

Creating Objects with EntityBuilder

Being able to dynamically create objects is a huge benefit to those languages that allow some sort of metaprogramming.  Imagine, if I were to take the time to write out 66 POCOs before actually working on the application.  We are talking easily a few days worth of work.  

Enter the entity builder.  While nothing fancy, I wound up with a class that will create a type given a name and a list of parameters.  You can see the full code here on gist, or just scroll to the bottom of the post.

The entity builder is used in the manner below, with ‘propertyNames’ being just an array of strings to call the properties.

var eb = new EntityBuilder();        
var newType = eb.CreateNewObject(name, propertyNames);        
object myNewObject = Activator.CreateInstance(newType);

This will create a property with the Type name of ‘name’ and all the properties will be strings with the supplied property names.   On the other end if you want more control you can specify the property types as well by sending in an IDictionary<string,Type> with string being their names and Type being the type you want them to be.  

Next all that’s left to do is to actually set the values of the properties in your object.  This is easy enough using reflection still, in the following manner:

Populating the object

PropertyInfo[] propertyInfos;
propertyInfos = unpopulatedObject.GetType().GetProperties();
foreach (PropertyInfo propertyInfo in propertyInfos)
{
 propertyInfo.SetValue(myNewObject,SOMEPROPERTYVALUEHERE);
}

As you can see it is fairly easy.  The way that I did this was that I had all of my information in a DataTable and then instead of SOMEPROPERTYHERE just referenced the column that I wanted to set.

After this you have a fully hydrated object, ready to use!

Entity Builder Code

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace EntityBuilder
{
internal class EntityBuilder
{
private readonly IDictionary<string, Type> _cache;
public EntityBuilder()
{
_cache = new Dictionary<string, Type>();
}
public Type CreateNewObject(string typeName, IDictionary<string, Type> propertyDefinitions)
{
if (_cache.ContainsKey(typeName))
return _cache[typeName];
return CompileResultType(typeName, propertyDefinitions);
}
public Type CreateNewObject(string typeName, string[] properties)
{
if (_cache.ContainsKey(typeName))
return _cache[typeName];
var propertyDefinitions = propertyNameToStringDefinition(properties);
return CompileResultType(typeName, propertyDefinitions);
}
private IDictionary<string, Type> propertyNameToStringDefinition(string[] properties)
{
var dict = new Dictionary<string, Type>();
foreach (var property in properties) dict.Add(property, typeof(string));
return dict;
}
private Type CompileResultType(string typeName, IDictionary<string, Type> properties)
{
var tb = GetTypeBuilder(typeName);
var constructor =
tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName |
MethodAttributes.RTSpecialName);
foreach (var field in properties)
CreateProperty(tb, field.Key, field.Value);
var objectType = tb.CreateType();
_cache.Add(typeName, objectType);
return objectType;
}
private TypeBuilder GetTypeBuilder(string typeName)
{
var typeSignature = typeName;
var an = new AssemblyName(typeSignature);
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
var tb = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
null);
return tb;
}
private void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
var fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
var propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
var getPropMthdBldr = tb.DefineMethod("get_" + propertyName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType,
Type.EmptyTypes);
var getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
var setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[]{propertyType});
var setIl = setPropMthdBldr.GetILGenerator();
var modifyProperty = setIl.DefineLabel();
var exitSet = setIl.DefineLabel();
setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}
}
view raw EntityBuilder.cs hosted with ❤ by GitHub


Categories: General, Programming

Tags: , , , ,

1 reply

Trackbacks

  1. Loading Blazor Components at run-time – DCCoder

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: