diff Source/DataAccess/DataAccessorBuilder.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/DataAccess/DataAccessorBuilder.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,2107 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+
+using BLToolkit.Common;
+using BLToolkit.Data;
+using BLToolkit.Data.DataProvider;
+using BLToolkit.Mapping;
+using BLToolkit.Properties;
+using BLToolkit.Reflection;
+using BLToolkit.Reflection.Emit;
+using BLToolkit.TypeBuilder;
+using BLToolkit.TypeBuilder.Builders;
+
+namespace BLToolkit.DataAccess
+{
+	public class DataAccessorBuilder : AbstractTypeBuilderBase
+	{
+		struct MapOutputParametersValue
+		{
+			public readonly string ReturnValueMember;
+			public readonly ParameterInfo ParameterInfo;
+
+			public MapOutputParametersValue(string returnValueMember, ParameterInfo parameterInfo)
+			{
+				ReturnValueMember = returnValueMember;
+				ParameterInfo = parameterInfo;
+			}
+		}
+
+		public override int GetPriority(BuildContext context)
+		{
+			return TypeBuilderConsts.Priority.DataAccessor;
+		}
+
+		public override bool IsApplied(BuildContext context, AbstractTypeBuilderList builders)
+		{
+			if (context.IsBuildStep)
+			{
+				if (context.IsAbstractMethod)
+				{
+					// Give up if there is any builder that builds the method body.
+					//
+					if (builders.Count > 1)
+						foreach (IAbstractTypeBuilder builder in builders)
+							if (builder != this && builder.IsApplied(context, builders))
+								return false;
+
+					return true;
+				}
+
+				// Treat an abstract getter/setter as a regular method
+				// when the property has [NoInstance] attribute 
+				//
+				if (context.IsAbstractGetter || context.IsAbstractSetter)
+					return context.CurrentProperty.IsDefined(typeof(NoInstanceAttribute), true);
+			}
+
+			return false;
+		}
+
+		private Dictionary<Type, Type> _actualTypes;
+		private Dictionary<Type, Type> ActualTypes
+		{
+			get
+			{
+				if (_actualTypes == null)
+				{
+					_actualTypes = new Dictionary<Type, Type>();
+
+					object[] attrs = Context.Type.GetAttributes(typeof(ActualTypeAttribute));
+
+					foreach (ActualTypeAttribute attr in attrs)
+						if (!_actualTypes.ContainsKey(attr.BaseType))
+							_actualTypes.Add(attr.BaseType, attr.ActualType);
+				}
+
+				return _actualTypes;
+			}
+		}
+
+		enum ReturnType
+		{
+			DataReader,
+			DataSet,
+			DataTable,
+			List,
+			Dictionary,
+			Enumerable,
+			Void,
+			Scalar,
+			Object
+		}
+
+		static ReturnType GetReturnType(Type returnType)
+		{
+			if (returnType == typeof(IDataReader))
+				return ReturnType.DataReader;
+
+			if (returnType == typeof(DataSet) || returnType.IsSubclassOf(typeof(DataSet)))
+				return ReturnType.DataSet;
+
+			if (returnType == typeof(DataTable) || returnType.IsSubclassOf(typeof(DataTable)))
+				return ReturnType.DataTable;
+
+			if (!returnType.IsArray &&
+				(IsInterfaceOf(returnType, typeof(IList)) ||
+					returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(IList<>)))
+				return ReturnType.List;
+
+			if (IsInterfaceOf(returnType, typeof(IDictionary)) ||
+				returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+				return ReturnType.Dictionary;
+
+			if (returnType == typeof(IEnumerable) ||
+				returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
+				return ReturnType.Enumerable;
+
+			if (returnType == typeof(void))
+				return ReturnType.Void;
+
+			if (TypeHelper.IsScalar(returnType.IsByRef ? returnType.GetElementType() : returnType))
+				return ReturnType.Scalar;
+
+			return ReturnType.Object;
+		}
+
+		void ThrowTypeBuilderException(string message)
+		{
+			throw new TypeBuilderException(
+				string.Format(message, Context.CurrentMethod.DeclaringType.Name, Context.CurrentMethod.Name));
+		}
+
+		const BindingFlags _bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
+
+		readonly Type     _baseType = typeof(DataAccessor);
+		Type              _objectType;
+		bool              _explicitObjectType;
+		ParameterInfo[]   _parameters;
+		ArrayList         _paramList;
+		ArrayList         _refParamList;
+		bool              _createManager;
+		LocalBuilder      _locManager;
+		LocalBuilder      _locObjType;
+		ArrayList         _outputParameters;
+		SqlQueryAttribute _sqlQueryAttribute;
+		ArrayList         _formatParamList;
+		ParameterInfo     _destination;
+		ArrayList         _mapOutputParameters;
+
+		protected override void BuildAbstractMethod()
+		{
+			// Any class variable must be initialized before use
+			// as the same instance of the class is utilized to build abstract methods.
+			//
+			_paramList           = new ArrayList();
+			_refParamList        = new ArrayList();
+			_formatParamList     = new ArrayList();
+			_mapOutputParameters = new ArrayList();
+			_destination         = null;
+			_createManager       = true;
+			_objectType          = null;
+			_explicitObjectType  = false;
+			_parameters          = Context.CurrentMethod.GetParameters();
+			_locManager          = Context.MethodBuilder.Emitter.DeclareLocal(typeof(DbManager));
+			_locObjType          = Context.MethodBuilder.Emitter.DeclareLocal(typeof(Type));
+			_outputParameters    = null;
+			_sqlQueryAttribute   = null;
+
+			GetSqlQueryAttribute();
+			ProcessParameters();
+
+			var returnType = MethodReturnType;
+			var rt         = GetReturnType(returnType);
+
+			CreateDbManager(rt != ReturnType.Enumerable);
+			SetObjectType();
+
+			// Define execution method type.
+			//
+			switch (rt)
+			{
+				case ReturnType.DataReader : ExecuteReader();            break;
+				case ReturnType.DataSet    : ExecuteDataSet(returnType); break;
+				case ReturnType.DataTable  : ExecuteDataTable();         break;
+				case ReturnType.Void       : ExecuteNonQuery();          break;
+				case ReturnType.Scalar     : ExecuteScalar();            break;
+				case ReturnType.Enumerable : ExecuteEnumerable();        break;
+
+				case ReturnType.List:
+
+					if (!_explicitObjectType)
+					{
+						Type elementType = TypeHelper.GetListItemType(returnType);
+
+						if (elementType == typeof(object) && _destination != null)
+							elementType = TypeHelper.GetListItemType(Context.CurrentMethod.ReturnType);
+
+						if (elementType != typeof(object))
+							_objectType = elementType;
+
+						if (ActualTypes.ContainsKey(_objectType))
+							_objectType = ActualTypes[_objectType];
+					}
+
+					if (_objectType == null || _objectType == typeof(object))
+						ThrowTypeBuilderException(Resources.DataAccessorBuilder_BadListItemType);
+
+					if (TypeHelper.IsScalar(_objectType))
+						ExecuteScalarList();
+					else
+						ExecuteList();
+
+					break;
+
+				case ReturnType.Dictionary:
+					{
+						Type elementType = null;
+						Type keyType = typeof(object);
+						Type[] gTypes = TypeHelper.GetGenericArguments(returnType, typeof(IDictionary));
+
+						if ((gTypes == null || gTypes.Length != 2) && _destination != null)
+							gTypes = TypeHelper.GetGenericArguments(_destination.ParameterType, typeof(IDictionary));
+
+						if (gTypes != null && gTypes.Length == 2)
+						{
+							keyType = gTypes[0];
+							elementType = gTypes[1];
+						}
+
+						if (elementType == null || _explicitObjectType)
+							elementType = _objectType;
+
+						if (elementType == null || elementType == typeof(object))
+							ThrowTypeBuilderException(Resources.DataAccessorBuilder_BadListItemType);
+
+						bool isIndex = TypeHelper.IsSameOrParent(typeof(CompoundValue), keyType);
+
+						if (keyType != typeof(object) && !isIndex && !TypeHelper.IsScalar(keyType))
+							ThrowTypeBuilderException(
+								Resources.DataAccessorBuilder_BadKeyType);
+
+						MethodInfo mi = Context.CurrentMethod;
+
+						object[] attrs = mi.GetCustomAttributes(typeof(IndexAttribute), true);
+						NameOrIndexParameter[] fields = new NameOrIndexParameter[0];
+
+						if (attrs.Length != 0)
+							fields = ((IndexAttribute)attrs[0]).Fields;
+
+						if (fields.Length > 1 && keyType != typeof(object) && !isIndex)
+							ThrowTypeBuilderException(
+								Resources.DataAccessor_InvalidKeyType);
+
+						if (TypeHelper.IsScalar(elementType))
+						{
+							attrs = mi.GetCustomAttributes(typeof(ScalarFieldNameAttribute), true);
+
+							if (attrs.Length == 0)
+								ThrowTypeBuilderException(Resources.DataAccessorBuilder_ScalarFieldNameMissing);
+
+							NameOrIndexParameter scalarField = ((ScalarFieldNameAttribute)attrs[0]).NameOrIndex;
+
+							if (fields.Length == 0)
+								ExecuteScalarDictionaryWithPK(keyType, scalarField, elementType);
+							else if (isIndex || fields.Length > 1)
+								ExecuteScalarDictionaryWithMapIndex(fields, scalarField, elementType);
+							else
+								ExecuteScalarDictionaryWithScalarKey(fields[0], keyType, scalarField, elementType);
+						}
+						else
+						{
+							if (!_explicitObjectType && ActualTypes.ContainsKey(elementType))
+								elementType = ActualTypes[elementType];
+
+							if (fields.Length == 0)
+								ExecuteDictionaryWithPK(keyType, elementType);
+							else if (isIndex || fields.Length > 1)
+								ExecuteDictionaryWithMapIndex(fields, elementType);
+							else
+								ExecuteDictionaryWithScalarKey(fields[0], elementType);
+						}
+					}
+
+					break;
+
+				default:
+
+					if (_objectType == null || !TypeHelper.IsSameOrParent(returnType, _objectType))
+						_objectType = returnType;
+
+					if (!_explicitObjectType && ActualTypes.ContainsKey(_objectType))
+						_objectType = ActualTypes[_objectType];
+
+					ExecuteObject();
+
+					break;
+			}
+
+			GetOutRefParameters();
+
+			if (rt != ReturnType.Enumerable)
+				Finally();
+		}
+
+		protected override void BuildAbstractGetter()
+		{
+			BuildAbstractMethod();
+		}
+
+		protected override void BuildAbstractSetter()
+		{
+			BuildAbstractMethod();
+		}
+
+		void GetSqlQueryAttribute()
+		{
+			object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(SqlQueryAttribute), true);
+
+			if (attrs.Length != 0)
+				_sqlQueryAttribute = (SqlQueryAttribute)attrs[0];
+		}
+
+		void AddParameter(ParameterInfo pi)
+		{
+			Type pType = pi.ParameterType;
+
+			if (pType.IsByRef)
+				pType = pType.GetElementType();
+
+			if (TypeHelper.IsScalar(pType)
+#if FW4
+				|| pi.IsRefCursor()
+#endif
+				)
+				_paramList.Add(pi);
+			else if (pType == typeof(DbManager) || pType.IsSubclassOf(typeof(DbManager)))
+				_createManager = false;
+			else
+				_refParamList.Add(pi);
+		}
+
+		void ProcessParameters()
+		{
+			for (int i = 0; i < _parameters.Length; i++)
+			{
+				ParameterInfo pi = _parameters[i];
+				NoMapAttribute[] attrs = (NoMapAttribute[])pi.GetCustomAttributes(typeof(NoMapAttribute), true);
+
+				if (attrs.Length == 0)
+					AddParameter(pi);
+				else
+				{
+					for (int j = 0; j < attrs.Length; ++j)
+					{
+						if (!attrs[j].NoMap)
+							AddParameter(pi);
+
+						if (attrs[j] is FormatAttribute)
+						{
+							int index = ((FormatAttribute)attrs[j]).Index;
+
+							if (index < 0)
+								index = 0;
+							else if (index > _formatParamList.Count)
+								index = _formatParamList.Count;
+
+							_formatParamList.Insert(index, pi);
+						}
+						else if (attrs[j] is DestinationAttribute)
+						{
+							if (_destination != null)
+								throw new TypeBuilderException(Resources.DataAccessorBuilderTooManyDestinations);
+
+							_destination = pi;
+						}
+					}
+				}
+			}
+		}
+
+		void CreateDbManager(bool beginException)
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+
+			if (_createManager)
+			{
+				emit
+					.ldarg_0
+					.callvirt(_baseType, "GetDbManager")
+					.stloc(_locManager);
+
+				if (beginException)
+					emit.BeginExceptionBlock();
+			}
+			else
+			{
+				for (int i = 0; i < _parameters.Length; i++)
+				{
+					Type pType = _parameters[i].ParameterType;
+
+					if (pType == typeof(DbManager) || pType.IsSubclassOf(typeof(DbManager)))
+					{
+						emit
+							.ldarg(_parameters[i])
+							.stloc(_locManager)
+							;
+
+						break;
+					}
+				}
+			}
+		}
+
+		void SetObjectType()
+		{
+			var mi    = Context.CurrentMethod;
+			var attrs = mi.GetCustomAttributes(typeof(ObjectTypeAttribute), true);
+
+			if (attrs.Length == 0)
+				attrs = mi.DeclaringType.GetCustomAttributes(typeof(ObjectTypeAttribute), true);
+			else
+				_explicitObjectType = true;
+
+			if (attrs.Length != 0)
+				_objectType = ((ObjectTypeAttribute)attrs[0]).ObjectType;
+
+			if (_objectType == null)
+			{
+				var types = TypeHelper.GetGenericArguments(mi.DeclaringType, typeof(DataAccessor));
+
+				if (types != null)
+					_objectType = types[0];
+			}
+		}
+
+		#region ExecuteReader
+
+		void ExecuteReader()
+		{
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+
+			var attrs = Context.CurrentMethod.GetCustomAttributes(typeof(CommandBehaviorAttribute), true);
+
+			if (attrs.Length == 0)
+			{
+				Context.MethodBuilder.Emitter
+					.callvirt(typeof(DbManager).GetMethod("ExecuteReader", Type.EmptyTypes))
+					.stloc(Context.ReturnValue)
+					;
+			}
+			else
+			{
+				Context.MethodBuilder.Emitter
+					.ldc_i4_((int)((CommandBehaviorAttribute)attrs[0]).CommandBehavior)
+					.callvirt(typeof(DbManager), "ExecuteReader", typeof(CommandBehavior))
+					.stloc(Context.ReturnValue)
+					;
+			}
+		}
+
+		#endregion
+
+		#region ExecuteDataSet
+
+		void ExecuteDataSet(Type returnType)
+		{
+			CreateReturnTypeInstance();
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+
+			var emit = Context.MethodBuilder.Emitter;
+
+			if (returnType == typeof(DataSet))
+			{
+				LoadDestinationOrReturnValue();
+
+				object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(DataSetTableAttribute), true);
+
+				if (attrs.Length == 0)
+				{
+					emit
+						.callvirt(typeof(DbManager), "ExecuteDataSet", typeof(DataSet))
+						.pop
+						.end()
+						;
+				}
+				else
+				{
+					emit
+						.ldNameOrIndex(((DataSetTableAttribute)attrs[0]).NameOrIndex)
+						.callvirt(typeof(DbManager), "ExecuteDataSet", typeof(DataSet), typeof(NameOrIndexParameter))
+						.pop
+						.end()
+						;
+				}
+			}
+			else
+			{
+				emit
+					.pop
+					.end()
+					;
+
+				LoadDestinationOrReturnValue();
+
+				Label l1 = emit.DefineLabel();
+				Label l2 = emit.DefineLabel();
+
+				emit
+					.callvirt(typeof(DataSet).GetProperty("Tables").GetGetMethod())
+					.callvirt(typeof(InternalDataCollectionBase).GetProperty("Count").GetGetMethod())
+					.ldc_i4_0
+					.ble_s(l1)
+					.ldloc(_locManager);
+
+				LoadDestinationOrReturnValue();
+
+				object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(DataSetTableAttribute), true);
+
+				if (attrs.Length == 0)
+				{
+					LoadDestinationOrReturnValue();
+
+					emit
+						.callvirt(typeof(DataSet).GetProperty("Tables").GetGetMethod())
+						.ldc_i4_0
+						.callvirt(typeof(DataTableCollection), "get_Item", typeof(int))
+						.callvirt(typeof(DataTable).GetProperty("TableName").GetGetMethod())
+						.call(typeof(NameOrIndexParameter), "op_Implicit", typeof(string))
+						;
+				}
+				else
+				{
+					emit
+						.ldNameOrIndex(((DataSetTableAttribute)attrs[0]).NameOrIndex)
+						;
+				}
+
+				emit
+					.callvirt(typeof(DbManager), "ExecuteDataSet", typeof(DataSet), typeof(NameOrIndexParameter))
+					.pop
+					.br_s(l2)
+					.MarkLabel(l1)
+					.ldloc(_locManager);
+
+				LoadDestinationOrReturnValue();
+
+				emit
+					.callvirt(typeof(DbManager), "ExecuteDataSet", typeof(DataSet))
+					.pop
+					.MarkLabel(l2)
+					;
+			}
+		}
+
+		#endregion
+
+		#region ExecuteDataTable
+
+		void ExecuteDataTable()
+		{
+			CreateReturnTypeInstance();
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+			LoadDestinationOrReturnValue();
+
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+
+			emit
+				.callvirt(typeof(DbManager), "ExecuteDataTable", typeof(DataTable))
+				.pop
+				.end()
+				;
+
+			// When DataSetTableAttribute is present, simply set table name to the name specified.
+			//
+			object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(DataSetTableAttribute), true);
+
+			if (attrs.Length != 0)
+			{
+				DataSetTableAttribute attr = (DataSetTableAttribute)attrs[0];
+
+				if (!attr.NameOrIndex.ByName)
+					throw new TypeBuilderException(string.Format(
+						Resources.DataAccessorBuilder_DataSetTableMustBeByName,
+						Context.CurrentMethod.DeclaringType.Name, Context.CurrentMethod.Name));
+
+				LoadDestinationOrReturnValue();
+
+				emit
+					.ldstr(attr.NameOrIndex.Name)
+					.callvirt(typeof(DataTable), "set_TableName", typeof(string))
+					;
+			}
+		}
+
+		#endregion
+
+		#region ExecuteScalarList
+
+		void ExecuteScalarList()
+		{
+			CreateReturnTypeInstance();
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+			LoadDestinationOrReturnValue();
+
+			object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(ScalarFieldNameAttribute), true);
+
+			if (attrs.Length == 0)
+			{
+				Context.MethodBuilder.Emitter
+					.ldloc(_locObjType)
+					.callvirt(typeof(DbManager), "ExecuteScalarList", typeof(IList), typeof(Type))
+					.pop
+					.end()
+					;
+			}
+			else
+			{
+				Context.MethodBuilder.Emitter
+					.ldloc(_locObjType)
+					.ldNameOrIndex(((ScalarFieldNameAttribute)attrs[0]).NameOrIndex)
+					.callvirt(typeof(DbManager), "ExecuteScalarList", typeof(IList), typeof(Type), typeof(NameOrIndexParameter))
+					.pop
+					.end()
+					;
+			}
+		}
+
+		void ExecuteList()
+		{
+			CreateReturnTypeInstance();
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+			LoadDestinationOrReturnValue();
+
+			Context.MethodBuilder.Emitter
+				.CastIfNecessary(typeof(IList), MethodReturnType)
+				.ldloc(_locObjType)
+				.callvirt(typeof(DbManager), "ExecuteList", typeof(IList), typeof(Type))
+				.pop
+				.end()
+				;
+		}
+
+		#endregion
+
+		#region ExecuteDictionary
+
+		public FieldBuilder GetIndexField(NameOrIndexParameter[] namesOrIndexes)
+		{
+			var id = "index$" + string.Join("%",
+				Array.ConvertAll<NameOrIndexParameter, string>(namesOrIndexes,
+					delegate(NameOrIndexParameter nameOrIndex)
+					{
+						return nameOrIndex.ToString();
+					}));
+
+			var fieldBuilder = Context.GetField(id);
+
+			if (fieldBuilder == null)
+			{
+				fieldBuilder = Context.CreatePrivateStaticField(id, typeof(MapIndex));
+
+				EmitHelper emit = Context.TypeBuilder.TypeInitializer.Emitter;
+
+				emit
+					.ldc_i4_(namesOrIndexes.Length)
+					.newarr(typeof(NameOrIndexParameter))
+					;
+
+				for (int i = 0; i < namesOrIndexes.Length; i++)
+				{
+					emit
+						.dup
+						.ldc_i4_(i)
+						.ldelema(typeof(NameOrIndexParameter));
+
+					if (namesOrIndexes[i].ByName)
+					{
+						emit
+							.ldstr(namesOrIndexes[i].Name)
+							.call(typeof(NameOrIndexParameter), "op_Implicit", typeof(string));
+					}
+					else
+					{
+						emit
+							.ldc_i4_(namesOrIndexes[i].Index)
+							.call(typeof(NameOrIndexParameter), "op_Implicit", typeof(int));
+					}
+
+					emit
+						.stobj(typeof(NameOrIndexParameter))
+						.end()
+						;
+				}
+
+				emit
+					.newobj(typeof(MapIndex), typeof(NameOrIndexParameter[]))
+					.stsfld(fieldBuilder)
+					;
+			}
+
+			return fieldBuilder;
+		}
+
+		/// <summary>
+		/// Maps primary keys(s) to a scalar field.
+		/// </summary>
+		void ExecuteScalarDictionaryWithPK(
+			Type keyType,
+			NameOrIndexParameter scalarField,
+			Type elementType)
+		{
+			CreateReturnTypeInstance();
+			InitObjectType();
+
+			Context.MethodBuilder.Emitter
+				.ldarg_0
+				.end()
+				;
+
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+			LoadDestinationOrReturnValue();
+
+			Context.MethodBuilder.Emitter
+				.ldloc(_locObjType)
+				.LoadType(keyType)
+				.ldstr(Context.CurrentMethod.Name)
+				.ldNameOrIndex(scalarField)
+				.LoadType(elementType)
+				.callvirt(_baseType, "ExecuteScalarDictionary", _bindingFlags,
+								typeof(DbManager), typeof(IDictionary), typeof(Type),
+								typeof(Type), typeof(string), typeof(NameOrIndexParameter), typeof(Type))
+				;
+		}
+
+		/// <summary>
+		/// Maps a complex index to a scalar field.
+		/// </summary>
+		void ExecuteScalarDictionaryWithMapIndex(
+			NameOrIndexParameter[] index,
+			NameOrIndexParameter scalarField,
+			Type elementType)
+		{
+			_objectType = elementType;
+
+			CreateReturnTypeInstance();
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+			LoadDestinationOrReturnValue();
+
+			Context.MethodBuilder.Emitter
+				.ldsfld(GetIndexField(index))
+				.ldNameOrIndex(scalarField)
+				.ldloc(_locObjType)
+				.callvirt(typeof(DbManager), "ExecuteScalarDictionary",
+								typeof(IDictionary), typeof(MapIndex),
+								typeof(NameOrIndexParameter), typeof(Type))
+				.pop
+				.end()
+				;
+		}
+
+		/// <summary>
+		/// Maps any single field to any (other) single field.
+		/// </summary>
+		void ExecuteScalarDictionaryWithScalarKey(
+			NameOrIndexParameter keyField, Type keyType,
+			NameOrIndexParameter scalarField, Type elementType)
+		{
+			_objectType = elementType;
+
+			CreateReturnTypeInstance();
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+			LoadDestinationOrReturnValue();
+
+			Context.MethodBuilder.Emitter
+				.ldNameOrIndex(keyField)
+				.LoadType(keyType)
+				.ldNameOrIndex(scalarField)
+				.ldloc(_locObjType)
+				.callvirt(typeof(DbManager), "ExecuteScalarDictionary",
+								typeof(IDictionary), typeof(NameOrIndexParameter), typeof(Type),
+								typeof(NameOrIndexParameter), typeof(Type))
+				.pop
+				.end()
+				;
+		}
+
+		/// <summary>
+		/// Maps primary keys(s) to an object of the specified type.
+		/// </summary>
+		void ExecuteDictionaryWithPK(
+			Type keyType,
+			Type elementType)
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+
+			_objectType = elementType;
+
+			CreateReturnTypeInstance();
+			InitObjectType();
+
+			emit
+				.ldarg_0
+				.end()
+				;
+
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+			LoadDestinationOrReturnValue();
+
+			if (IsGenericDestinationOrReturnValue())
+			{
+				Type[] genericArgs = Context.ReturnValue.LocalType.GetGenericArguments();
+				Type[] types = new Type[]
+					{
+						typeof(DbManager),
+						typeof(IDictionary<,>).MakeGenericType(genericArgs),
+						typeof(Type),
+						typeof(string),
+					};
+				MethodInfo method = _baseType.GetMethod("ExecuteDictionary",
+					_bindingFlags, GenericBinder.Generic, types, null);
+
+				if (TypeHelper.IsSameOrParent(typeof(CompoundValue), genericArgs[0]))
+					method = method.MakeGenericMethod(genericArgs[1]);
+				else
+					method = method.MakeGenericMethod(genericArgs);
+
+				emit
+					.ldloc(_locObjType)
+					.ldstr(Context.CurrentMethod.Name)
+					.callvirt(method)
+					;
+			}
+			else
+
+				emit
+					.ldloc(_locObjType)
+					.LoadType(keyType)
+					.ldstr(Context.CurrentMethod.Name)
+					.callvirt(_baseType, "ExecuteDictionary", _bindingFlags,
+								typeof(DbManager), typeof(IDictionary), typeof(Type),
+								typeof(Type), typeof(string))
+					;
+		}
+
+		/// <summary>
+		/// Maps a complex index to an object of the specified type.
+		/// </summary>
+		void ExecuteDictionaryWithMapIndex(
+			NameOrIndexParameter[] index,
+			Type elementType)
+		{
+			_objectType = elementType;
+
+			CreateReturnTypeInstance();
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+			LoadDestinationOrReturnValue();
+
+			Context.MethodBuilder.Emitter
+				.ldsfld(GetIndexField(index))
+				.ldloc(_locObjType)
+				.ldnull
+				.callvirt(typeof(DbManager), "ExecuteDictionary",
+							typeof(IDictionary), typeof(MapIndex), typeof(Type), typeof(object[]))
+				.pop
+				.end()
+				;
+		}
+
+		/// <summary>
+		/// Maps any single field to object type.
+		/// </summary>
+		void ExecuteDictionaryWithScalarKey(
+			NameOrIndexParameter keyField,
+			Type elementType)
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+
+			_objectType = elementType;
+
+			CreateReturnTypeInstance();
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+			LoadDestinationOrReturnValue();
+
+			if (IsGenericDestinationOrReturnValue())
+			{
+				Type[] genericArgs = Context.ReturnValue.LocalType.GetGenericArguments();
+				Type[] types = new Type[]
+					{
+						typeof(IDictionary<,>).MakeGenericType(genericArgs),
+						typeof(NameOrIndexParameter),
+						typeof(Type),
+						typeof(object[]),
+					};
+				MethodInfo method = typeof(DbManager).GetMethod("ExecuteDictionary", _bindingFlags, GenericBinder.Generic, types, null)
+					.MakeGenericMethod(genericArgs);
+
+				emit
+					.ldNameOrIndex(keyField)
+					.ldloc(_locObjType)
+					.ldnull
+					.callvirt(method)
+					.pop
+					.end()
+					;
+			}
+			else
+			{
+				emit
+					.ldNameOrIndex(keyField)
+					.ldloc(_locObjType)
+					.ldnull
+					.callvirt(typeof(DbManager), "ExecuteDictionary", typeof(IDictionary), typeof(NameOrIndexParameter), typeof(Type), typeof(object[]))
+					.pop
+					.end()
+					;
+			}
+		}
+
+		#endregion
+
+		#region ExecuteEnumerable
+
+		public void ExecuteEnumerable()
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+			Type returnType = Context.CurrentMethod.ReturnType;
+
+			if (_objectType == null && returnType.IsGenericType)
+				_objectType = returnType.GetGenericArguments()[0];
+
+			if (_objectType == null || _objectType == typeof(object))
+				ThrowTypeBuilderException(Resources.DataAccessorBuilder_BadListItemType);
+
+			Type returnObjectType = returnType.IsGenericType ? returnType.GetGenericArguments()[0] : _objectType;
+
+			InitObjectType();
+
+			Context.MethodBuilder.Emitter
+				.ldarg_0
+				.end()
+				;
+
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+
+			Type[] genericArgs = new Type[] { returnObjectType };
+			Type[] types = new Type[] { typeof(DbManager), typeof(Type), typeof(bool), };
+			MethodInfo method = _baseType
+				.GetMethod("ExecuteEnumerable", _bindingFlags, GenericBinder.Generic, types, null)
+				.MakeGenericMethod(genericArgs);
+
+			emit
+				.LoadType(_objectType)
+				.ldc_i4_1
+				.callvirt(method)
+				.stloc(Context.ReturnValue)
+				;
+		}
+
+		#endregion
+
+		#region ExecuteNonQuery
+
+		public void ExecuteNonQuery()
+		{
+			if (_destination != null)
+				throw new TypeBuilderException(Resources.DataAccessorBuilder_CantExecuteNonQueryToDestination);
+
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+
+			MethodInfo mi = typeof(DbManager).GetMethod("ExecuteNonQuery", Type.EmptyTypes);
+			LocalBuilder locExec = Context.MethodBuilder.Emitter.DeclareLocal(mi.ReturnType);
+
+			Context.MethodBuilder.Emitter
+				.callvirt(mi)
+				.stloc(locExec)
+				;
+
+			if (Context.ReturnValue != null)
+			{
+				Context.MethodBuilder.Emitter
+					.ldloc(locExec)
+					.stloc(Context.ReturnValue)
+					;
+			}
+		}
+
+		#endregion
+
+		#region ExecuteScalar
+
+		public void ExecuteScalar()
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+			Type returnType = Context.CurrentMethod.ReturnType;
+			Type scalarType;
+
+			if (_destination != null)
+			{
+				if (_destination.ParameterType.IsByRef)
+					scalarType = _destination.ParameterType.GetElementType();
+				else
+					throw new TypeBuilderException(Resources.DataAccessorBuilder_ScalarDestinationIsNotByRef);
+
+				if (returnType != typeof(void) && !TypeHelper.IsSameOrParent(returnType, scalarType))
+				{
+					// object Foo(out int num) is valid,
+					// IConvertible Foo(ref int num) is also ok,
+					// but string Bar(out DateTime dt) is not
+					//
+					throw new TypeBuilderException(string.Format(
+						Resources.DataAccessorBuilder_IncompatibleDestinationType,
+						returnType.FullName, Context.CurrentMethod.Name, scalarType.FullName));
+				}
+			}
+			else
+				scalarType = returnType;
+
+			if (_destination != null)
+				emit
+					.ldarg(_destination)
+					;
+
+			emit
+				.ldarg_0
+				.ldloc(_locManager)
+				;
+
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+
+			object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(ScalarSourceAttribute), true);
+
+			if (attrs.Length == 0)
+			{
+				emit
+					.callvirtNoGenerics(typeof(DbManager), "ExecuteScalar")
+					;
+			}
+			else
+			{
+				ScalarSourceAttribute attr = (ScalarSourceAttribute)attrs[0];
+
+				emit
+					.ldc_i4_((int)attr.ScalarType)
+					.ldNameOrIndex(attr.NameOrIndex)
+					.callvirtNoGenerics(typeof(DbManager), "ExecuteScalar", typeof(ScalarSourceType), typeof(NameOrIndexParameter));
+			}
+
+			MethodInfo converter = GetConverterMethod(scalarType);
+
+			if (converter == null)
+			{
+				emit
+					.LoadType(scalarType)
+					.ldnull
+					.callvirt(_baseType, "ConvertChangeType", _bindingFlags, typeof(DbManager), typeof(object), typeof(Type), typeof(object))
+					.unboxIfValueType(scalarType)
+					;
+
+			}
+			else
+			{
+				emit
+					.ldnull
+					.callvirt(converter)
+					;
+			}
+
+			if (_destination != null)
+			{
+				emit
+					.stind(scalarType)
+					;
+
+				// The return value and a destination both are present
+				//
+				if (Context.ReturnValue != null)
+				{
+					emit
+						.ldargEx(_destination, false)
+						;
+
+					if (scalarType != returnType)
+						emit
+							.boxIfValueType(scalarType)
+							.CastFromObject(returnType)
+							;
+
+					emit.stloc(Context.ReturnValue)
+					;
+				}
+			}
+			else
+				emit
+					.stloc(Context.ReturnValue)
+					;
+		}
+
+		#endregion
+
+		#region ExecuteObject
+
+		public void ExecuteObject()
+		{
+			InitObjectType();
+			GetSprocNameOrSqlQueryTest();
+			CallSetCommand();
+
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+
+			if (_destination != null)
+			{
+				emit
+					.ldarg(_destination)
+					.callvirt(typeof(DbManager), "ExecuteObject", typeof(Object))
+					;
+			}
+			else
+			{
+				emit
+					.ldloc(_locObjType)
+					.callvirt(typeof(DbManager), "ExecuteObject", typeof(Type))
+					;
+			}
+
+			if (null != Context.ReturnValue)
+			{
+				emit
+					.castclass(_objectType)
+					.stloc(Context.ReturnValue)
+					;
+			}
+			else
+			{
+				emit
+					.pop
+					.end()
+					;
+			}
+		}
+
+		#endregion
+
+		void Finally()
+		{
+			if (_createManager)
+			{
+				Context.MethodBuilder.Emitter
+					.BeginFinallyBlock()
+					.ldarg_0
+					.ldloc(_locManager)
+					.callvirt(_baseType, "Dispose", _bindingFlags, typeof(DbManager))
+					.EndExceptionBlock()
+					;
+			}
+		}
+
+		void CreateReturnTypeInstance()
+		{
+			if (null == Context.ReturnValue)
+				return;
+
+			if (null != _destination)
+			{
+				Context.MethodBuilder.Emitter
+					.ldarg(_destination)
+					.CastIfNecessary(Context.ReturnValue.LocalType, _destination.ParameterType)
+					.stloc(Context.ReturnValue)
+					;
+			}
+			else
+			{
+				Type returnType = Context.CurrentMethod.ReturnType;
+
+				if (returnType.IsInterface)
+				{
+					if (IsInterfaceOf(returnType, typeof(IList)))
+						returnType = typeof(ArrayList);
+					else if (IsInterfaceOf(returnType, typeof(IDictionary)))
+						returnType = typeof(Hashtable);
+					else if (returnType.GetGenericTypeDefinition() == typeof(IList<>))
+						returnType = typeof(List<>).MakeGenericType(returnType.GetGenericArguments());
+					else if (returnType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
+						returnType = typeof(Dictionary<,>).MakeGenericType(returnType.GetGenericArguments());
+				}
+
+				ConstructorInfo ci = TypeHelper.GetDefaultConstructor(returnType);
+
+				if (ci == null)
+					throw new TypeBuilderException(string.Format(Resources.DataAccessorBuilder_CantCreateTypeInstance,
+						Context.CurrentMethod.ReturnType.FullName));
+
+				Context.MethodBuilder.Emitter
+					.newobj(ci)
+					.stloc(Context.ReturnValue)
+					;
+			}
+		}
+
+		Type MethodReturnType
+		{
+			get
+			{
+				return _destination != null ?
+					_destination.ParameterType :
+					Context.CurrentMethod.ReturnType;
+			}
+		}
+
+		void LoadDestinationOrReturnValue()
+		{
+			if (_destination != null)
+				Context.MethodBuilder.Emitter.ldarg(_destination);
+			else
+				Context.MethodBuilder.Emitter.ldloc(Context.ReturnValue);
+		}
+
+		bool IsGenericDestinationOrReturnValue()
+		{
+			return _destination == null ?
+				Context.ReturnValue.LocalType.IsGenericType :
+				_destination.ParameterType.IsGenericType;
+		}
+
+		void InitObjectType()
+		{
+			Context.MethodBuilder.Emitter
+				.LoadType(_objectType)
+				.stloc(_locObjType)
+				;
+		}
+
+		static int _nameCounter;
+		static int _uniqueQueryID;
+
+		void GetSprocNameOrSqlQueryTest()
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+
+			if (_sqlQueryAttribute != null)
+			{
+				emit
+					.ldloc(_locManager)
+					;
+
+				if (_sqlQueryAttribute.ID != int.MinValue)
+				{
+					emit
+						.ldarg_0
+						.ldloc(_locManager)
+						.ldc_i4_(_sqlQueryAttribute.ID)
+						.ldc_i4_(++_uniqueQueryID)
+						;
+				}
+
+				if (_sqlQueryAttribute.IsDynamic)
+				{
+					Type attrType = typeof(SqlQueryAttribute);
+					FieldBuilder field = Context.CreatePrivateStaticField(attrType + "$" + ++_nameCounter, attrType);
+					Label isNull = emit.DefineLabel();
+
+					emit
+						.ldsfld(field)
+						.brtrue_s(isNull)
+
+						.ldarg_0
+						.call(typeof(MethodBase), "GetCurrentMethod")
+						.castclass(typeof(MethodInfo))
+						.callvirt(_baseType, "GetSqlQueryAttribute", _bindingFlags, typeof(MethodInfo))
+
+						.stsfld(field)
+						.MarkLabel(isNull)
+
+						.ldsfld(field)
+						.ldarg_0
+						.ldloc(_locManager)
+						.callvirt(attrType, "GetSqlText", _bindingFlags, typeof(DataAccessor), typeof(DbManager))
+						;
+				}
+				else
+				{
+					emit
+						.ldstr(_sqlQueryAttribute.SqlText)
+						;
+				}
+
+				if (_sqlQueryAttribute.ID != int.MinValue)
+				{
+					emit
+						.callvirt(_baseType, "PrepareSqlQuery", _bindingFlags,
+							typeof(DbManager), typeof(int), typeof(int), typeof(string))
+						;
+				}
+			}
+			else
+			{
+				object[] attrs = Context.CurrentMethod.GetCustomAttributes(typeof(SprocNameAttribute), true);
+
+				if (attrs.Length == 0)
+				{
+					attrs = Context.CurrentMethod.GetCustomAttributes(typeof(ActionNameAttribute), true);
+
+					string actionName = attrs.Length == 0 ?
+						Context.CurrentMethod.Name : ((ActionNameAttribute)attrs[0]).Name;
+
+					// Call GetSpName.
+					//
+					emit
+						.ldloc(_locManager)
+						.ldarg_0
+						.ldloc(_locObjType)
+						.ldstr(actionName)
+						.callvirt(_baseType, "GetSpName", _bindingFlags, typeof(Type), typeof(string))
+						;
+				}
+				else
+				{
+					emit
+						.ldloc(_locManager)
+						.ldstr(((SprocNameAttribute)attrs[0]).Name)
+						;
+				}
+			}
+
+			// string.Format
+			//
+			if (_formatParamList.Count > 0)
+			{
+				emit
+					.ldc_i4_(_formatParamList.Count)
+					.newarr(typeof(object))
+					;
+
+				for (int i = 0; i < _formatParamList.Count; i++)
+				{
+					ParameterInfo pi = (ParameterInfo)_formatParamList[i];
+
+					emit
+						.dup
+						.ldc_i4_(i)
+						.ldarg(pi)
+						.boxIfValueType(pi.ParameterType)
+						.stelem_ref
+						.end()
+						;
+				}
+
+				emit
+					.call(typeof(string), "Format", typeof(string), typeof(object[]))
+					;
+			}
+		}
+
+		void CallSetCommand()
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+
+			// Get DiscoverParametersAttribute.
+			//
+			object[] attrs =
+				Context.CurrentMethod.DeclaringType.GetCustomAttributes(typeof(DiscoverParametersAttribute), true);
+
+			bool discoverParams = false;
+
+			if (_sqlQueryAttribute == null)
+			{
+				discoverParams = attrs.Length == 0 ?
+					false : ((DiscoverParametersAttribute)attrs[0]).Discover;
+
+				attrs = Context.CurrentMethod.GetCustomAttributes(typeof(DiscoverParametersAttribute), true);
+
+				if (attrs.Length != 0)
+					discoverParams = ((DiscoverParametersAttribute)attrs[0]).Discover;
+			}
+
+			LocalBuilder locParams = discoverParams ?
+				BuildParametersWithDiscoverParameters() :
+				BuildParameters();
+
+			// Call SetSpCommand.
+			//
+			string methodName = _sqlQueryAttribute == null ? "SetSpCommand" : "SetCommand";
+			Type paramType = _sqlQueryAttribute == null ? typeof(object[]) : typeof(IDbDataParameter[]);
+
+			emit
+				.ldloc(locParams)
+				.callvirt(typeof(DbManager), methodName, _bindingFlags, typeof(string), paramType)
+				;
+		}
+
+		LocalBuilder BuildParameters()
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+
+			LocalBuilder retParams = emit
+				.DeclareLocal(typeof(IDbDataParameter[]));
+
+			LocalBuilder locParams = _refParamList.Count > 0 ?
+				BuildRefParameters() :
+				BuildSimpleParameters();
+
+			emit
+				.ldarg_0
+				.ldloc(_locManager)
+				.ldloc(locParams)
+				.callvirt(_baseType, "PrepareParameters", _bindingFlags, typeof(DbManager), typeof(object[]))
+				.stloc(retParams)
+				;
+
+			return retParams;
+		}
+
+		LocalBuilder BuildSimpleParameters()
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+
+			// Parameters.
+			//
+			LocalBuilder locParams = emit.DeclareLocal(
+				_sqlQueryAttribute == null ? typeof(object[]) : typeof(IDbDataParameter[]));
+
+			emit
+				.ldc_i4_(_paramList.Count)
+				.newarr(_sqlQueryAttribute == null ? typeof(object) : typeof(IDbDataParameter))
+				;
+
+			for (int i = 0; i < _paramList.Count; i++)
+			{
+				ParameterInfo pi = (ParameterInfo)_paramList[i];
+
+				emit
+					.dup
+					.ldc_i4_(i)
+					;
+
+				BuildParameter(pi);
+
+				emit
+					.stelem_ref
+					.end()
+					;
+			}
+
+			emit.stloc(locParams);
+			return locParams;
+		}
+
+		FieldBuilder CreateStringArrayField(object[] attrs)
+		{
+			if (attrs.Length == 0)
+				return null;
+
+			List<string> list = new List<string>();
+
+			foreach (Direction attr in attrs)
+				if (attr.Members != null)
+					list.AddRange(attr.Members);
+
+			if (list.Count == 0)
+				return null;
+
+			list.Sort(string.CompareOrdinal);
+
+			string[] strings = list.ToArray();
+
+			// There a no limit for a field name length, but Visual Studio Debugger
+			// may crash on fields with name longer then 256 symbols.
+			//
+			string key = "_string_array$" + string.Join("%", strings);
+			FieldBuilder fieldBuilder = Context.GetField(key);
+
+			if (null == fieldBuilder)
+			{
+				fieldBuilder = Context.CreatePrivateStaticField(key, typeof(string[]));
+
+				EmitHelper emit = Context.TypeBuilder.TypeInitializer.Emitter;
+
+				emit
+					.ldc_i4_(strings.Length)
+					.newarr(typeof(string))
+					;
+
+				for (int i = 0; i < strings.Length; i++)
+				{
+					emit
+						.dup
+						.ldc_i4_(i)
+						.ldstr(strings[i])
+						.stelem_ref
+						.end()
+						;
+				}
+
+				emit
+					.stsfld(fieldBuilder)
+					;
+			}
+
+			return fieldBuilder;
+		}
+
+		FieldBuilder CreateNullValueField(Type type, string value)
+		{
+			string key = "_null_value$" + type.FullName + "%" + value;
+			FieldBuilder fieldBuilder = Context.GetField(key);
+
+			if (null == fieldBuilder)
+			{
+				fieldBuilder = Context.CreatePrivateStaticField(key, type);
+
+				EmitHelper emit = Context.TypeBuilder.TypeInitializer.Emitter;
+
+				emit
+					.LoadType(type)
+					.call(typeof(TypeDescriptor), "GetConverter", typeof(Type))
+					.ldstr(value)
+					.callvirt(typeof(TypeConverter), "ConvertFromInvariantString", typeof(string))
+					.unbox_any(type)
+					.stsfld(fieldBuilder)
+					;
+			}
+
+			return fieldBuilder;
+		}
+
+		LocalBuilder BuildRefParameters()
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+
+			// Parameters.
+			//
+			LocalBuilder locParams = emit.DeclareLocal(typeof(object[]));
+
+			emit
+				.ldc_i4_(_parameters.Length)
+				.newarr(typeof(object))
+				;
+
+			for (int i = 0; i < _parameters.Length; i++)
+			{
+				ParameterInfo pi = _parameters[i];
+
+				emit
+					.dup
+					.ldc_i4_(i)
+					;
+
+				if (_paramList.Contains(pi))
+				{
+					BuildParameter(pi);
+				}
+				else if (_refParamList.Contains(pi))
+				{
+					var          mapOutputParameters = false;
+					string       returnValueMember = null;
+					FieldBuilder fieldBuilder;
+					var          type =
+						pi.ParameterType == typeof(DataRow) || pi.ParameterType.IsSubclassOf(typeof(DataRow)) ?
+							typeof(DataRow) : typeof(object);
+
+					emit
+						.ldarg_0
+						.ldloc(_locManager)
+						.ldarg(pi)
+						;
+
+					fieldBuilder = CreateStringArrayField(pi.GetCustomAttributes(typeof(Direction.OutputAttribute), true));
+
+					if (fieldBuilder != null)
+					{
+						emit.ldsfld(fieldBuilder);
+						mapOutputParameters = true;
+					}
+					else
+						emit.ldnull.end();
+
+					fieldBuilder = CreateStringArrayField(pi.GetCustomAttributes(typeof(Direction.InputOutputAttribute), true));
+
+					if (fieldBuilder != null)
+					{
+						emit.ldsfld(fieldBuilder);
+						mapOutputParameters = true;
+					}
+					else
+						emit.ldnull.end();
+
+					fieldBuilder = CreateStringArrayField(pi.GetCustomAttributes(typeof(Direction.IgnoreAttribute), true));
+
+					if (fieldBuilder != null)
+						emit.ldsfld(fieldBuilder);
+					else
+						emit.ldnull.end();
+
+					emit
+						.ldnull
+						.callvirt(_baseType, "CreateParameters", _bindingFlags,
+							typeof(DbManager), type, typeof(string[]), typeof(string[]), typeof(string[]), typeof(IDbDataParameter[]))
+						;
+
+					object[] attrs = pi.GetCustomAttributes(typeof(Direction.ReturnValueAttribute), true);
+
+					if (attrs.Length != 0)
+						returnValueMember = ((Direction.ReturnValueAttribute)attrs[0]).Member;
+
+					if (null != returnValueMember || mapOutputParameters)
+						_mapOutputParameters.Add(new MapOutputParametersValue(returnValueMember, pi));
+				}
+				else
+				{
+					emit
+						.ldnull
+						.end()
+						;
+				}
+
+				emit
+					.stelem_ref
+					.end()
+					;
+			}
+
+			emit.stloc(locParams);
+			return locParams;
+		}
+
+		void LoadParameterOrNull(ParameterInfo pi, Type type)
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+			object[] attrs = pi.GetCustomAttributes(typeof(ParamNullValueAttribute), true);
+
+			object nullValue = attrs.Length == 0 ?
+				null : ((ParamNullValueAttribute)attrs[0]).Value;
+
+			Label labelNull = emit.DefineLabel();
+			Label labelEndIf = emit.DefineLabel();
+
+			if (pi.Attributes == ParameterAttributes.Out)
+			{
+				emit
+					.ldnull
+					.end()
+					;
+
+				return;
+			}
+
+			if (nullValue != null)
+			{
+				Type nullValueType = type;
+				bool isNullable = TypeHelper.IsNullable(type);
+
+				if (type.IsEnum)
+				{
+					nullValueType = Enum.GetUnderlyingType(type);
+					nullValue = System.Convert.ChangeType(nullValue, nullValueType);
+				}
+				else if (isNullable)
+				{
+					nullValueType = type.GetGenericArguments()[0];
+
+					emit
+						.ldarga(pi)
+						.call(type, "get_HasValue")
+						.brfalse(labelNull)
+						;
+				}
+
+				if (nullValueType == nullValue.GetType() && emit.LoadWellKnownValue(nullValue))
+				{
+					if (nullValueType == typeof(string))
+						emit
+							.ldargEx(pi, false)
+							.call(nullValueType, "Equals", nullValueType)
+							.brtrue(labelNull)
+							;
+					else if (isNullable)
+						emit
+							.ldarga(pi)
+							.call(type, "get_Value")
+							.beq(labelNull)
+							;
+					else
+						emit
+							.ldargEx(pi, false)
+							.beq(labelNull)
+						;
+				}
+				else
+				{
+					string nullString = TypeDescriptor.GetConverter(nullValue).ConvertToInvariantString(nullValue);
+					FieldBuilder staticField = CreateNullValueField(nullValueType, nullString);
+					MethodInfo miEquals = new TypeHelper(nullValueType).GetPublicMethod("Equals", nullValueType);
+
+					if (miEquals == null)
+					{
+						// Is it possible?
+						//
+						throw new TypeBuilderException(string.Format(
+							Resources.DataAccessorBuilder_EqualsMethodIsNotPublic, type.FullName));
+					}
+
+					if (isNullable)
+						emit
+							.ldsflda(staticField)
+							.ldarga(pi)
+							.call(pi.ParameterType, "get_Value")
+							;
+					else
+						emit
+							.ldsflda(staticField)
+							.ldarg(pi)
+						;
+
+					if (miEquals.GetParameters()[0].ParameterType.IsClass)
+						emit
+							.boxIfValueType(nullValueType)
+							;
+
+					emit
+						.call(miEquals)
+						.brtrue(labelNull)
+						;
+				}
+			}
+
+			if (type.IsEnum)
+				emit
+					.ldloc(_locManager)
+					.callvirt(typeof(DbManager).GetProperty("MappingSchema").GetGetMethod())
+					;
+
+			emit
+				.ldargEx(pi, true)
+				;
+
+			if (type.IsEnum)
+				emit
+					.ldc_i4_1
+					.callvirt(typeof(MappingSchema), "MapEnumToValue", typeof(object), typeof(bool))
+					;
+
+			if (nullValue != null)
+			{
+				emit
+					.br(labelEndIf)
+					.MarkLabel(labelNull)
+					.ldnull
+					.MarkLabel(labelEndIf)
+					;
+			}
+		}
+
+		void BuildParameter(ParameterInfo pi)
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+			Type type = pi.ParameterType;
+			object[] attrs = pi.GetCustomAttributes(typeof(ParamNameAttribute), true);
+			string paramName = attrs.Length == 0 ? pi.Name : ((ParamNameAttribute)attrs[0]).Name;
+
+			ParameterDirection direction = !type.IsByRef ? ParameterDirection.Input :
+				pi.IsOut ? ParameterDirection.Output : ParameterDirection.InputOutput;
+
+			emit
+				.ldloc(_locManager)
+				.ldc_i4_((int)direction)
+				;
+
+			if (paramName[0] != '@')
+			{
+				string methodName = _sqlQueryAttribute == null ? "GetSpParameterName" : "GetQueryParameterName";
+				emit
+					.ldarg_0
+					.ldloc(_locManager)
+					.ldstr(paramName)
+					.callvirt(_baseType, methodName, _bindingFlags, typeof(DbManager), typeof(string))
+					;
+			}
+			else
+				emit.ldstr(paramName);
+
+			if (type.IsByRef)
+			{
+				if (_outputParameters == null)
+					_outputParameters = new ArrayList();
+
+				_outputParameters.Add(pi);
+
+				type = type.GetElementType();
+			}
+
+			LoadParameterOrNull(pi, type);
+
+			// Special case for user-defined types.
+			//
+			attrs = pi.GetCustomAttributes(typeof(ParamTypeNameAttribute), true);
+			if (attrs.Length > 0)
+			{
+				emit
+					.ldstr(((ParamTypeNameAttribute)attrs[0]).TypeName)
+					.callvirt(typeof(DbManager), "Parameter",
+										typeof(ParameterDirection), typeof(string), typeof(object), typeof(string))
+					;
+			}
+			else
+			{
+				emit
+					.callvirt(typeof(DbManager), "Parameter",
+										typeof(ParameterDirection), typeof(string), typeof(object))
+					;
+			}
+
+			// Check if parameter type/size is specified.
+			//
+			attrs = pi.GetCustomAttributes(typeof(ParamDbTypeAttribute), true);
+			if (attrs.Length > 0)
+			{
+				emit
+					.dup
+					.ldc_i4_((int)((ParamDbTypeAttribute)attrs[0]).DbType)
+					.callvirt(typeof(IDataParameter), "set_DbType", typeof(DbType))
+					;
+			}
+
+			attrs = pi.GetCustomAttributes(typeof(ParamSizeAttribute), true);
+			if (attrs.Length > 0)
+			{
+				emit
+					.dup
+					.ldc_i4_(((ParamSizeAttribute)attrs[0]).Size)
+					.callvirt(typeof(IDbDataParameter), "set_Size", typeof(int))
+					;
+			}
+		}
+
+		LocalBuilder BuildParametersWithDiscoverParameters()
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+			LocalBuilder locParams = emit.DeclareLocal(typeof(object[]));
+
+			emit
+				.ldc_i4_(_paramList.Count)
+				.newarr(typeof(object))
+				;
+
+			for (int i = 0; i < _paramList.Count; i++)
+			{
+				ParameterInfo pi = (ParameterInfo)_paramList[i];
+
+				emit
+					.dup
+					.ldc_i4_(i)
+					;
+
+				LoadParameterOrNull(pi, pi.ParameterType);
+
+				emit
+					.stelem_ref
+					.end()
+					;
+			}
+
+			emit.stloc(locParams);
+			return locParams;
+		}
+
+		void StoreParameterValue(LocalBuilder param, ParameterInfo pi, Type type)
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+			Label labelNull = emit.DefineLabel();
+			Label labelEndIf = emit.DefineLabel();
+
+			object[] attrs = pi.GetCustomAttributes(typeof(ParamNullValueAttribute), true);
+			object nullValue = attrs.Length == 0 ? null : ((ParamNullValueAttribute)attrs[0]).Value;
+
+			if (nullValue != null)
+			{
+				emit
+					.ldarg_0
+					.ldloc(_locManager)
+					.ldloc(param)
+					.callvirt(typeof(IDataParameter).GetProperty("Value").GetGetMethod())
+					.ldloc(param)
+					.callvirt(_baseType, "IsNull", _bindingFlags, typeof(DbManager), typeof(object), typeof(object))
+					.brtrue(labelNull)
+					;
+			}
+
+			if (type.IsEnum)
+			{
+				emit
+					.ldloc(_locManager)
+					.callvirt(typeof(DbManager).GetProperty("MappingSchema").GetGetMethod())
+					.ldloc(param)
+					.callvirt(typeof(IDataParameter).GetProperty("Value").GetGetMethod())
+					.LoadType(type)
+					.callvirt(typeof(MappingSchema), "MapValueToEnum", typeof(object), typeof(Type))
+					.CastFromObject(type)
+					;
+			}
+#if FW4
+			else if (pi.IsRefCursor())
+			{
+				// Make sure the parameter is a List
+				if (!type.GetInterfaces().Contains(typeof(IList)))
+				{
+					throw new Exception("The argument '" + pi.Name + "' must be of type 'IList'");
+				}
+				//Get the generic type of the list
+				Type genericType = type.GetGenericArguments().First();
+
+
+				// Get the data reader to the ref cursor
+				var dataReader = emit.DeclareLocal(typeof(IDataReader));
+				emit
+					.ldloc(_locManager)
+					.callvirt(typeof(DbManager).GetProperty("DataProvider").GetGetMethod())
+					.ldloc(param)
+					.callvirt(typeof(IDataParameter).GetProperty("Value").GetGetMethod())
+					.callvirt(typeof(DataProviderBase), "GetRefCursorDataReader", typeof(object))
+					.CastFromObject(typeof(IDataReader))
+					.stloc(dataReader)
+					;
+
+				// Create the generic methos info to invoke
+				var mapDataReaderToListMethodInfo = typeof (MappingSchema).GetMethod("MapDataReaderToList",
+																						new[]
+																							{
+																								typeof (IDataReader),
+																								typeof (object[])
+																							})
+					.MakeGenericMethod(genericType);
+
+				// Run MapDataReaderToList
+				emit
+					.ldloc(_locManager)
+					.callvirt(typeof(DbManager).GetProperty("MappingSchema").GetGetMethod())
+					.ldloc(dataReader)
+					.ldnull
+					.callvirt(mapDataReaderToListMethodInfo)
+					;
+			}
+#endif
+			else
+			{
+				emit
+					.ldarg_0
+					.ldloc(_locManager)
+					.ldloc(param)
+					.callvirt(typeof(IDataParameter).GetProperty("Value").GetGetMethod())
+					;
+
+				MethodInfo converter = GetConverterMethod(type);
+
+				if (converter == null)
+				{
+					emit
+						.LoadType(type)
+						.ldloc(param)
+						.callvirt(_baseType, "ConvertChangeType", _bindingFlags, typeof(DbManager), typeof(object), typeof(Type), typeof(object))
+						.unboxIfValueType(type)
+						;
+				}
+				else
+				{
+					emit
+						.ldloc(param)
+						.callvirt(converter)
+						;
+				}
+			}
+
+			if (nullValue != null)
+			{
+				emit
+					.br(labelEndIf)
+					.MarkLabel(labelNull);
+
+				if (nullValue.GetType() != type || !emit.LoadWellKnownValue(nullValue))
+				{
+					string nullString = TypeDescriptor.GetConverter(type).ConvertToInvariantString(nullValue);
+					FieldBuilder staticField = CreateNullValueField(type, nullString);
+
+					emit
+						.ldsfld(staticField)
+						;
+				}
+
+				emit
+					.MarkLabel(labelEndIf)
+					;
+			}
+
+			emit.stind(type);
+		}
+
+		void GetOutRefParameters()
+		{
+			EmitHelper emit = Context.MethodBuilder.Emitter;
+
+			if (_outputParameters != null)
+			{
+				LocalBuilder param = emit.DeclareLocal(typeof(IDataParameter));
+
+				foreach (ParameterInfo pi in _outputParameters)
+				{
+					Type type = pi.ParameterType.GetElementType();
+
+					emit
+						.ldarg(pi)
+						;
+
+					// Get parameter.
+					//
+					object[] attrs = pi.GetCustomAttributes(typeof(ParamNameAttribute), true);
+
+					string paramName = attrs.Length == 0 ?
+						pi.Name : ((ParamNameAttribute)attrs[0]).Name;
+
+					emit
+						.ldarg_0
+						.ldloc(_locManager)
+						;
+
+					if (paramName[0] != '@')
+					{
+						string methodName = _sqlQueryAttribute == null ? "GetSpParameterName" : "GetQueryParameterName";
+
+						emit
+							.ldarg_0
+							.ldloc(_locManager)
+							.ldstr(paramName)
+							.callvirt(_baseType, methodName, _bindingFlags, typeof(DbManager), typeof(string))
+							;
+					}
+					else
+						emit.ldstr(paramName);
+
+					emit
+						.callvirt(_baseType, "GetParameter", _bindingFlags, typeof(DbManager), typeof(string))
+						.stloc(param)
+						;
+
+					StoreParameterValue(param, pi, type);
+				}
+			}
+
+			foreach (MapOutputParametersValue v in _mapOutputParameters)
+			{
+				emit
+					.ldloc(_locManager)
+					.ldstrEx(v.ReturnValueMember)
+					.ldarg(v.ParameterInfo)
+					.callvirt(typeof(DbManager), "MapOutputParameters", typeof(string), typeof(object));
+			}
+		}
+
+		static bool IsInterfaceOf(Type type, Type interfaceType)
+		{
+			Type[] types = type.GetInterfaces();
+
+			foreach (Type t in types)
+				if (t == interfaceType)
+					return true;
+
+			return type == interfaceType;
+		}
+
+		private MethodInfo GetConverterMethod(Type type)
+		{
+			if (type.IsEnum)
+				type = Enum.GetUnderlyingType(type);
+
+			Type[] types = new Type[] { typeof(DbManager), typeof(object), typeof(object) };
+			return _baseType.GetMethod("ConvertTo" + type.Name, _bindingFlags, null, types, null);
+		}
+	}
+}