Click here to Skip to main content
Click here to Skip to main content
Articles » Languages » C# » Reflection » Downloads
 
Add your own
alternative version
Go to top

Introduction to Creating Dynamic Types with Reflection.Emit: Part 2

, 1 May 2006
Part 2 of an introduction to creating dynamic types. This article shows how to actually generate the methods in a dynamic type and how to call them.
using System;
using System.Windows.Forms;
using System.Threading;
using System.Data;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;

namespace EmitPerfTesting
{
	/// <summary>
	/// Summary description for DataRowAdapterFactory.
	/// </summary>
	public class DataRowAdapterFactory
	{		
		private static AssemblyBuilder assBuilder = null;
		private static ModuleBuilder modBuilder = null;
		private static Dictionary<string, IDataRowAdapter> adapters = new Dictionary<string, IDataRowAdapter>();

		public static IDataRowAdapter CreateDataRowAdapter(DataSet ds, string tableName)
		{
			IDataRowAdapter dra = null;

			if (ds.Tables.Count > 0)
			{
				foreach (DataTable dt in ds.Tables)
				{
					if (dt.TableName == tableName)
						dra = CreateDataRowAdapter(dt, true);
					else
						CreateDataRowAdapter(dt, false);
				}
			}

			return dra;
		}

		public static IDataRowAdapter CreateDataRowAdapter(DataTable dt)
		{
			return CreateDataRowAdapter(dt, true);
		}

		private static IDataRowAdapter CreateDataRowAdapter(DataTable dt, bool returnAdapter)
		{
			//return no adapter if no columns or no table name
			if (dt.Columns.Count == 0 || dt.TableName.Length == 0)
				return null;

			//check to see if type is already created
			if (adapters.ContainsKey(dt.TableName))
				return (IDataRowAdapter)adapters[dt.TableName];

			//Create assembly and module 
			GenerateAssemblyAndModule();

			//create new type for table name
			TypeBuilder typeBuilder = CreateType(modBuilder, "DataRowAdapter_" + dt.TableName.Replace(" ", ""));			

			//create get ordinal
			CreateGetOrdinal(typeBuilder, dt);

			Debug.WriteLine("Creating instance of adapter for table " + dt.TableName);
			
			Type draType = typeBuilder.CreateType();
			//assBuilder.Save(assBuilder.GetName().Name + ".dll");

			//Create an instance of the DataRowAdapter
			IDataRowAdapter dra = (IDataRowAdapter)Activator.CreateInstance(draType, true);

			//cache adapter instance
			adapters.Add(dt.TableName, dra);

			//if just initializing adapter, dont return instance
			if (!returnAdapter)
				return null;

			return dra;
		}

		private static void GenerateAssemblyAndModule()
		{
			if (assBuilder == null)
			{
				Debug.WriteLine("Creating AssemblyBuilder for DynamicDataRowAdapter assembly");
				AssemblyName draAssemblyName = new AssemblyName();
				draAssemblyName.Name = "DynamicDataRowAdapter";
				AppDomain thisDomain = Thread.GetDomain();
				//TODO:figure out parm list to use for isSynchronized parm = true;
				assBuilder = thisDomain.DefineDynamicAssembly(draAssemblyName, AssemblyBuilderAccess.Run);
				//assBuilder = thisDomain.DefineDynamicAssembly(draAssemblyName, AssemblyBuilderAccess.RunAndSave);

				Debug.WriteLine("Creating Module for DynamicDataRowAdapter assembly");
				modBuilder = assBuilder.DefineDynamicModule(assBuilder.GetName().Name, false);
				//modBuilder = assBuilder.	(assBuilder.GetName().Name, assBuilder.GetName().Name + ".dll", false);					
			}
		}

		private static TypeBuilder CreateType(ModuleBuilder modBuilder, string typeName)
		{
			Debug.WriteLine("Creating type for table " + typeName);

			TypeBuilder typeBuilder = modBuilder.DefineType(typeName,
				TypeAttributes.Public |
				TypeAttributes.Class |
				TypeAttributes.AutoClass |
				TypeAttributes.AnsiClass |
				TypeAttributes.BeforeFieldInit |
				TypeAttributes.AutoLayout,
				typeof(object),
				new Type[] { typeof(IDataRowAdapter) });

			return typeBuilder;
		}

		private static void CreateGetOrdinal(TypeBuilder typeBuilder, DataTable dt)
		{
			Debug.WriteLine("Creating method for GetOrdinal() with hash checks");
			int colIndex = 0;

			//create the needed type arrays
			Type[] oneStringArg = new Type[1] { typeof(string) };
			Type[] twoStringArg = new Type[2] { typeof(string), typeof(string) };
			Type[] threeStringArg = new Type[3] { typeof(string), typeof(string), typeof(string) };

			//create needed method and contructor info objects
			ConstructorInfo appExceptionCtor = typeof(ApplicationException).GetConstructor(oneStringArg);
			MethodInfo getHashCode = typeof(string).GetMethod("GetHashCode");
			MethodInfo stringConcat = typeof(string).GetMethod("Concat", threeStringArg);
			MethodInfo stringEquals = typeof(string).GetMethod("op_Equality", twoStringArg);

			//defind the method builder
			MethodBuilder method = typeBuilder.DefineMethod(
				"GetOrdinal", 
				MethodAttributes.Public | 
				MethodAttributes.HideBySig | 
				MethodAttributes.NewSlot | 
				MethodAttributes.Virtual | 
				MethodAttributes.Final, 
				typeof(Int32), oneStringArg);

			//create IL Generator
			ILGenerator il = method.GetILGenerator();

			//define return jump label
			System.Reflection.Emit.Label outLabel = il.DefineLabel();

			//define return jump table used for the many if statements
			System.Reflection.Emit.Label[] jumpTable =
				new System.Reflection.Emit.Label[dt.Columns.Count];

			if (AllUniqueHashValues(dt))
			{
				//create the return int index value, and hash value
				LocalBuilder colRetIndex = il.DeclareLocal(typeof(Int32));
				LocalBuilder parmHashValue = il.DeclareLocal(typeof(Int32));
				il.Emit(OpCodes.Ldarg_1);
				il.Emit(OpCodes.Callvirt, getHashCode);
				il.Emit(OpCodes.Stloc_1);

				foreach (DataColumn col in dt.Columns)
				{
					//define label
					jumpTable[colIndex] = il.DefineLabel();

					//compare the two hash codes
					il.Emit(OpCodes.Ldloc_1);
					il.Emit(OpCodes.Ldc_I4, col.ColumnName.GetHashCode());
					il.Emit(OpCodes.Bne_Un, jumpTable[colIndex]);

					//if equal, load the ordianal into loc0 and return
					il.Emit(OpCodes.Ldc_I4, col.Ordinal);
					il.Emit(OpCodes.Stloc_0);
					il.Emit(OpCodes.Br, outLabel);
					il.MarkLabel(jumpTable[colIndex]);

					colIndex++;
				}
			}
			else
			{
				//create the return int index value, and hash value
				LocalBuilder colRetIndex = il.DeclareLocal(typeof(Int32));

				foreach (DataColumn col in dt.Columns)
				{
					Debug.WriteLine("Creating accessor for column " + col.ColumnName + " at ordinal " + col.Ordinal.ToString());

					//define false label
					jumpTable[colIndex] = il.DefineLabel();

					//compare the two strings
					il.Emit(OpCodes.Ldarg_1);
					il.Emit(OpCodes.Ldstr, col.ColumnName);
					il.Emit(OpCodes.Call, stringEquals);
					il.Emit(OpCodes.Brfalse, jumpTable[colIndex]);

					//if equal, load the ordianal into loc0 and return
					il.Emit(OpCodes.Ldc_I4, col.Ordinal);
					il.Emit(OpCodes.Stloc_0);
					il.Emit(OpCodes.Br, outLabel);
					il.MarkLabel(jumpTable[colIndex]);

					colIndex++;
				}
			}

			//error handler if cant find column name						
			il.Emit(OpCodes.Ldstr, "Column '");
			il.Emit(OpCodes.Ldarg_1);
			il.Emit(OpCodes.Ldstr, "' not found");
			il.Emit(OpCodes.Callvirt, stringConcat);
			il.Emit(OpCodes.Newobj, appExceptionCtor);
			il.Emit(OpCodes.Throw);

			//label for if user found column name
			il.MarkLabel(outLabel);

			//return ordinal for column
			il.Emit(OpCodes.Ldloc_0);
			il.Emit(OpCodes.Ret);
		}

		private static bool AllUniqueHashValues(DataTable dt)
		{
			Dictionary<int, int> uniqueHashes = new Dictionary<int, int>(dt.Columns.Count);
			foreach (DataColumn col in dt.Columns)
			{
				int hash = col.ColumnName.GetHashCode();
				if (!uniqueHashes.ContainsKey(hash))
					uniqueHashes.Add(hash, hash);
				else
					return false;				
			}
			return true;				
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

jconwell

United States United States
I have been a professional developer since 1996. My experience comes from many different industries; Data Mining Software, Consulting, E-Commerce, Wholesale Operations, Clinical Software, Insurance, Energy.
 
I started programming in the military, trying to find better ways to analyze database data, eventually automating my entire job. Later, in college, I automated my way out of another job. This gave me the great idea to switch majors to the only thing that seemed natural…Programming!
Follow on   Twitter

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 1 May 2006
Article Copyright 2006 by jconwell
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid