Click here to Skip to main content
15,894,907 members
Articles / Programming Languages / JScript .NET

Stored Procedure Invocation Code Generator for VB, C# and JScript.NET

Rate me:
Please Sign up or sign in to vote.
4.92/5 (24 votes)
14 Jun 20038 min read 387.5K   3.4K   133  
Stored Procedure Invocation Code Generator for VB, C# and JScript.NET
//================================================================================
//
//  Copyright (c) objectnation GmbH
//
//================================================================================
//
//  Author          simon.wilson
//  Creation Date   12.09.2002
//  Creation Time   18:28:24
//  IDE Version     Microsoft Development Environment Enterprise Edition 7.00
//  OS Version      Microsoft Windows NT 5.1.2600.0
//
//================================================================================

#region Copyright � objectnation GmbH

// This software is provided 'as-is'. While the greatest care has been taken to 
// ensure that the software functions correctly, no guarantee can be made to this effect.
//
// objectnation grants permission to anyone to use this software for any purpose as long as 
// it is in compliance with the following restrictions:
//
// 1.	This software is the intellectual property and copyright of objectnation GmbH. 
//    Under no circumstances may the origin of the software be misrepresented.
//
// 2.	Should this software be used in the development of a commercial product, 
//    then an acknowledgement in that product's documentation is a requirement. 
//    The following text should be used:
//
//    This product contains code generated by objectnation SP/Invoke, 
//    copyright � objectnation GmbH (http://www.objectnation.com).
//
// 3.	The software may not be redistributed without the express written permission of 
//    the copyright holder, namely objectnation GmbH, Switzerland.

#endregion

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Data;
using System.Data.SqlClient;
using System.Collections;
using System.Reflection;

namespace Objectnation.SPInvoke
{
	public class StoredProcedureTypeGenerator : TypeGeneratorWithSource
	{
		CodeTypeReference _rowType;
		CodeTypeReference _collectionType;
		CodeExpression _factoryExpression;
		RowTypeGenerator _rowTypeGenerator;

		public StoredProcedureTypeGenerator(
			StoredProcedure storedProc,
			CodeDomProvider provider,
			string typeName,
			TypeAttributes visibility) : base(storedProc, provider, typeName, visibility)
		{
			if(storedProc.HasResultSet)
				throw new ApplicationException("Stored procedure " + storedProc.Name + " has a result set.");
		}

		//============================================================================

		public StoredProcedureTypeGenerator(
			StoredProcedure storedProc,
			CodeDomProvider provider,
			string typeName,
			TypeAttributes visibility,
			RowTypeGenerator rowTypeGenerator) : base(storedProc, provider, typeName, visibility)
		{
			if(rowTypeGenerator == null)
				throw new ArgumentNullException("rowTypeGenerator");

			_rowTypeGenerator = rowTypeGenerator;

			_rowType = new CodeTypeReference(RowTypeGenerator.TypeName);
			_collectionType = new CodeTypeReference(_rowTypeGenerator.CollectionTypeName);

			_factoryExpression = new CodeObjectCreateExpression(
				_rowTypeGenerator.TypeName, new CodeVariableReferenceExpression("reader"));
		}

		//============================================================================

		public StoredProcedureTypeGenerator(
			StoredProcedure storedProc,
			CodeDomProvider provider,
			string typeName,
			TypeAttributes visibility,
			CodeTypeReference rowType,
			CodeTypeReference collectionType) : base(storedProc, provider, typeName, visibility)
		{
			if(rowType == null)
				throw new ArgumentNullException("rowType");
			else if(collectionType == null)
				throw new ArgumentNullException("collectionType");

			_rowType = rowType;
			_collectionType = collectionType;

			_factoryExpression = new CodeObjectCreateExpression(
				_rowType, new CodeVariableReferenceExpression("reader"));			
		}

		//============================================================================

		public StoredProcedureTypeGenerator(
			StoredProcedure storedProc,
			CodeDomProvider provider,
			string typeName,
			TypeAttributes visibility,
			CodeTypeReference collectionType,
			CodeSnippetExpression factoryExpression) : base(storedProc, provider, typeName, visibility)
		{
			if(collectionType == null)
				throw new ArgumentNullException("collectionType");
			else if(factoryExpression == null)
				throw new ArgumentNullException("factoryExpression");

			_collectionType = collectionType;
			_factoryExpression = factoryExpression;
		}

		//============================================================================

		public StoredProcedureTypeGenerator() : base()
		{
		}

		//============================================================================

		public CodeExpression FactoryExpression
		{
			get { return _factoryExpression; }
			set { _factoryExpression = value; }
		}

		//============================================================================

		public RowTypeGenerator RowTypeGenerator
		{
			get { return _rowTypeGenerator; }
			set { _rowTypeGenerator = value; }
		}

		//============================================================================

		public override CodeTypeDeclarationCollection Generate()
		{
			CodeTypeDeclarationCollection types = new CodeTypeDeclarationCollection();

			// create a new class for the stored procedure

			CodeTypeDeclaration type = new CodeTypeDeclaration(TypeName);
			type.TypeAttributes = Visibility;

			// add StoredProcedure attribute:
			//
			// [ StoredProcedure("<name>") ]

			type.CustomAttributes.Add(new CodeAttributeDeclaration("StoredProcedure", 
				new CodeAttributeArgument [] { new CodeAttributeArgument(new CodePrimitiveExpression(Source.Name)) } ));

			// add comment pre-amble

			type.Comments.Add(new CodeCommentStatement("<summary>Wrapper class for stored procedure [" + Source.Name + "]</summary>", true));

			if(Source.Parameters.Count > 0)
			{
				type.Comments.Add(new CodeCommentStatement("<remarks>", true));
				type.Comments.Add(new CodeCommentStatement("Original stored procedure parameters:", true));
				type.Comments.Add(new CodeCommentStatement("<list type=\"table\">", true));
				type.Comments.Add(new CodeCommentStatement("<listheader><term>Name</term><description>Type</description></listheader>", true));

				foreach(StoredProcedureParameter param in Source.Parameters)
				{
					type.Comments.Add(new CodeCommentStatement("<item><term>" + param.Name + "</term><description>" + param.RawSqlTypeName + "</description></item>", true));
				}

				type.Comments.Add(new CodeCommentStatement("</list>", true));
				type.Comments.Add(new CodeCommentStatement("</remarks>", true));
			}

			// if provider supports nested types then add to sp class. otherwise
			// create at namespace level with name of the sp class + Result

			if(IsResultTypeNested)
				type.Members.Add(GenerateResultType());
			else
				types.Add(GenerateResultType());

			// add events

			ICodeGenerator generator = Provider.CreateGenerator();

			if(generator.Supports(GeneratorSupport.DeclareEvents))
				AddEvents(type.Members);

			// add methods

			type.Members.Add(GenerateInvokeMethod());
			type.Members.Add(GenerateConnectionOverride());
			type.Members.Add(GenerateTransactionOverride());

			if(_rowTypeGenerator != null)
			{
				// does the provider support nested types?

				if(!generator.Supports(GeneratorSupport.NestedTypes))
				{
					throw new ApplicationException(
						Provider.GetType().Name + " does not support nested types. Move nested rowClass elements out of storedProcedureClass element");
				}

				CodeTypeDeclarationCollection nestedTypes = _rowTypeGenerator.Generate();

				foreach(CodeTypeDeclaration nestedType in nestedTypes)
					type.Members.Add(nestedType);
			}

			types.Add(type);

			return types;
		}
	
		//============================================================================

		CodeTypeDeclaration GenerateResultType()
		{
			CodeTypeDeclaration type = new CodeTypeDeclaration(ResultTypeName);

			type.Comments.Add(new CodeCommentStatement("<summary>Result type for " + TypeName + ".</summary>", true));
			type.TypeAttributes = TypeAttributes.Public;

			// private int _returnValue;

			CodeMemberField field = new CodeMemberField(typeof(int), "_returnValue");
			field.Attributes = MemberAttributes.Private;
			field.InitExpression = new CodePrimitiveExpression(0);

			type.Members.Add(field);

			// private int _rowsAffected;

			field = new CodeMemberField(typeof(int), "_rowsAffected");
			field.Attributes = MemberAttributes.Private;
			field.InitExpression = new CodePrimitiveExpression(-1);

			type.Members.Add(field);

			// public int ReturnValue { get { return _returnValue; } }

			CodeMemberProperty prop = new CodeMemberProperty();

			prop.Comments.Add(new CodeCommentStatement("<summary>Returns the stored procedure's return value</summary>", true));
			prop.Type = new CodeTypeReference(typeof(int));
			prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
			prop.Name = "ReturnValue";
			prop.GetStatements.Add
			(
				new CodeMethodReturnStatement
				(
					new CodeFieldReferenceExpression
					(
						new CodeThisReferenceExpression(), 
						"_returnValue"
					)
				)
			);

			type.Members.Add(prop);

			// public int ReturnValue { get { return _returnValue; } }

			prop = new CodeMemberProperty();

			prop.Comments.Add(new CodeCommentStatement("<summary>Returns the number of rows affected by the stored procedure</summary>", true));
			prop.Type = new CodeTypeReference(typeof(int));
			prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
			prop.Name = "RowsAffected";
			prop.GetStatements.Add
			(
				new CodeMethodReturnStatement
				(
					new CodeFieldReferenceExpression
					(
						new CodeThisReferenceExpression(), 
						"_rowsAffected"
					)
				)
			);

			type.Members.Add(prop);

			// initialization constructor

			CodeConstructor ctor = new CodeConstructor();

			ctor.Comments.Add(new CodeCommentStatement("<summary>Initialization constructor</summary>", true));
			ctor.Attributes = MemberAttributes.Public;
			ctor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "returnValue"));
			ctor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(int), "rowsAffected"));
			ctor.Statements.Add
			(
				new CodeAssignStatement
				(
					new CodeFieldReferenceExpression
					(
						new CodeThisReferenceExpression(), 
						"_returnValue"
					),
					new CodeArgumentReferenceExpression("returnValue")
				)
			);

			ctor.Statements.Add
			(
				new CodeAssignStatement
				(
					new CodeFieldReferenceExpression
					(
						new CodeThisReferenceExpression(), 
						"_rowsAffected"
					),
					new CodeArgumentReferenceExpression("rowsAffected")
				)
			);

			if(Source.HasResultSet)
			{
				// private <CollectionType> _rows;

				field = new CodeMemberField(_collectionType, "_rows");
				field.Attributes = MemberAttributes.Private;
				field.InitExpression = new CodePrimitiveExpression(null);

				type.Members.Add(field);

				ctor.Parameters.Add(new CodeParameterDeclarationExpression(_collectionType, "rows"));
				ctor.Statements.Add
				(
					new CodeAssignStatement
					(
						new CodeFieldReferenceExpression
						(
							new CodeThisReferenceExpression(), 
							"_rows"
						),
						new CodeArgumentReferenceExpression("rows")
					)
				);

				// public <CollectionType> Rows { get { return _rows; } }

				prop = new CodeMemberProperty();

				prop.Comments.Add(new CodeCommentStatement("<summary>The rows which were returned by the stored procedure</summary>", true));
				prop.Type = _collectionType;
				prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
				prop.Name = "Rows";
				prop.GetStatements.Add
				(
					new CodeMethodReturnStatement
					(
						new CodeFieldReferenceExpression
						(
							new CodeThisReferenceExpression(), 
							"_rows"
						)
					)
				);

				type.Members.Add(prop);
			}

			type.Members.Add(ctor);

			return type;
		}

		//============================================================================

		void AppendParameterList(CodeParameterDeclarationExpressionCollection parameters)
		{
			foreach(StoredProcedureParameter param in Source.Parameters)
			{
				string paramName = param.CreateCodeCompatibleName(Provider);

				FieldDirection paramDir = FieldDirection.In;
				CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression(param.Type.ClientType, paramName);

				if(param.Direction == ParameterDirection.InputOutput || param.Direction == ParameterDirection.Output)
				{ 
					// OUTPUT parameters can also be in/out so always use ref

					paramDir = FieldDirection.Ref;
				}

				paramDecl.Direction = paramDir;

				parameters.Add(paramDecl);
			}
		}
	
		// ===========================================================================

		CodeMemberMethod GenerateInvokeMethod()
		{
			CodeMemberMethod invokeMethod = new CodeMemberMethod();

			invokeMethod.Comments.Add(new CodeCommentStatement("<summary>Invokes [" + Source.Name + "] on a connection with an optional local transaction (specify null if the connection does not have an active local transaction).</summary>", true));
			invokeMethod.ReturnType = new CodeTypeReference("Result");
			invokeMethod.Name = "Invoke";

			// 2003-04-28
			//
			// make Invoke(IDbConnection, IDbTransaction, ...) override public
			// (suggested by Efran Cobisi)

			invokeMethod.Attributes = MemberAttributes.Static | MemberAttributes.Public;
		
			invokeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IDbConnection), "connection"));
			invokeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IDbTransaction), "transaction"));

			AppendParameterList(invokeMethod.Parameters);

			// construct a new IDbCommand reference

			// 2003-05-01, simon.wilson
			//
			// intialize to null and assign to IDbConnection.CreateCommand return value within try block

			invokeMethod.Statements.Add(new CodeVariableDeclarationStatement(typeof(IDbCommand), "cmd", new CodePrimitiveExpression(null)));

			CodeVariableReferenceExpression cmdVariable = new CodeVariableReferenceExpression("cmd");

			// add a try-catch-finally clause for command object

			CodeTryCatchFinallyStatement cmdTry = new CodeTryCatchFinallyStatement();
			invokeMethod.Statements.Add(cmdTry);

			// if(cmd != null) { cmd.Dispose(); }

			CodeConditionStatement ifCmdNotNull = new CodeConditionStatement();
			ifCmdNotNull.Condition = new CodeBinaryOperatorExpression(cmdVariable, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
			ifCmdNotNull.TrueStatements.Add(new CodeMethodInvokeExpression(cmdVariable, "Dispose"));

			cmdTry.FinallyStatements.Add(ifCmdNotNull);

			// cmd = connection.CreateCommand();

			cmdTry.TryStatements.Add(new CodeAssignStatement(cmdVariable, new CodeMethodInvokeExpression(new CodeArgumentReferenceExpression("connection"), "CreateCommand")));

			// add if statement to assign transaction to command object if not null

			CodeStatement assignTransactionStatement = new CodeAssignStatement(
				new CodePropertyReferenceExpression(cmdVariable, "Transaction"),
				new CodeVariableReferenceExpression("transaction"));

			CodeConditionStatement conditionalStatement = new CodeConditionStatement(
				new CodeBinaryOperatorExpression(new CodeVariableReferenceExpression("transaction"), CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null)),
				assignTransactionStatement);

			cmdTry.TryStatements.Add(conditionalStatement);

			// assign command-type

			CodePropertyReferenceExpression cmdType = new CodePropertyReferenceExpression(cmdVariable, "CommandType");
			CodeFieldReferenceExpression enumRef = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("CommandType"), "StoredProcedure");

			cmdTry.TryStatements.Add(new CodeAssignStatement(cmdType, enumRef));
			cmdTry.TryStatements.Add(new CodeAssignStatement(
				new CodePropertyReferenceExpression(cmdVariable, "CommandText"), new CodePrimitiveExpression(Source.Name)));

			// add return value parameter

			cmdTry.TryStatements.Add(new CodeCommentStatement("Prepare @RETURN_VALUE parameter"));
			CodeVariableDeclarationStatement returnValueParam = new CodeVariableDeclarationStatement(typeof(IDbDataParameter), "returnValueParam", new CodeMethodInvokeExpression(cmdVariable, "CreateParameter"));
			cmdTry.TryStatements.Add(returnValueParam);

			CodeVariableReferenceExpression returnValueParamVariable = new CodeVariableReferenceExpression("returnValueParam");

			// returnValueParam.ParameterName = @<returnValue>;

			cmdTry.TryStatements.Add(new CodeAssignStatement(
				new CodePropertyReferenceExpression(returnValueParamVariable, "ParameterName"), new CodePrimitiveExpression(Source.ReturnValue.Name)));

			// returnValueParam.DbType = System.Data.DbType.Int32;

			cmdTry.TryStatements.Add(new CodeAssignStatement(
				new CodePropertyReferenceExpression(returnValueParamVariable, "DbType"), 
				new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(DbType)), Source.ReturnValue.Type.DbType.ToString())));

			// returnValueParam.Direction = ParameterDirection.ReturnValue;

			CodeExpression directionAssignmentLeft = new CodePropertyReferenceExpression(returnValueParamVariable, "Direction");
			CodeExpression directionAssignmentRight = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression("ParameterDirection"), Source.ReturnValue.Direction.ToString());

			cmdTry.TryStatements.Add(new CodeAssignStatement(directionAssignmentLeft, directionAssignmentRight));

			// cmd.Parameters.Add(returnValueParam);

			cmdTry.TryStatements.Add(new CodeMethodInvokeExpression(
				new CodePropertyReferenceExpression(cmdVariable, "Parameters"),
				"Add", 
				new CodeExpression [] { returnValueParamVariable } ));

			// add parameters

			foreach(StoredProcedureParameter param in Source.Parameters)
			{
				cmdTry.TryStatements.Add(new CodeCommentStatement("Prepare " + param.Name + " parameter"));

				string paramName = param.CreateCodeCompatibleName(Provider);

				// IDbParameter param = cmd.CreateParameter();

				CodeVariableDeclarationStatement sqlParamInstantiation = new CodeVariableDeclarationStatement(
					typeof(IDbDataParameter),
					paramName + "Param",
					new CodeMethodInvokeExpression(cmdVariable, "CreateParameter"));

				cmdTry.TryStatements.Add(sqlParamInstantiation);

				CodeVariableReferenceExpression paramVariable = new CodeVariableReferenceExpression(paramName + "Param");

				// param.ParameterName = @<name>;

				cmdTry.TryStatements.Add(new CodeAssignStatement(
					new CodePropertyReferenceExpression(paramVariable, "ParameterName"), new CodePrimitiveExpression(param.Name)));

				// param.DbType = System.Data.DbType.<type>;

				cmdTry.TryStatements.Add(new CodeAssignStatement(
					new CodePropertyReferenceExpression(paramVariable, "DbType"), 
					new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(DbType)), param.Type.DbType.ToString())));

				// param.Size = <size>;

				cmdTry.TryStatements.Add(new CodeAssignStatement(
					new CodePropertyReferenceExpression(paramVariable, "Size"), 
					new CodePrimitiveExpression(param.Size)));

				// param.Direction = <direction>;

				cmdTry.TryStatements.Add(new CodeAssignStatement(
					new CodePropertyReferenceExpression(paramVariable, "Direction"), 
					new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(ParameterDirection)), param.Direction.ToString())));

				// cmd.Parameters.Add(param);

				cmdTry.TryStatements.Add(new CodeMethodInvokeExpression(
					new CodePropertyReferenceExpression(cmdVariable, "Parameters"),
					"Add", 
					new CodeExpression [] { paramVariable } ));

				// assign value

				CodeConditionStatement ifNull = new CodeConditionStatement
				(
					new CodeBinaryOperatorExpression
					(
						new CodePropertyReferenceExpression
						(
							new CodeArgumentReferenceExpression(paramName),
							"IsNull"
						),
						CodeBinaryOperatorType.ValueEquality,
						new CodePrimitiveExpression(true)
					)
				);

				// <param>Param.Value = System.DBNull.Value;

				ifNull.TrueStatements.Add
				(
					new CodeAssignStatement
					(
						new CodePropertyReferenceExpression
						(
							new CodeVariableReferenceExpression(paramName + "Param"),
							"Value"
						),
						new CodeFieldReferenceExpression	
						(
							new CodeTypeReferenceExpression(typeof(DBNull)),
							"Value"
						)
					)
				);

				// <param>Param.Value = <param>.Value;

				ifNull.FalseStatements.Add
				(
					new CodeAssignStatement
					(
						new CodePropertyReferenceExpression
						(
							new CodeVariableReferenceExpression(paramName + "Param"),
							"Value"
						),
						new CodeFieldReferenceExpression	
						(
							new CodeArgumentReferenceExpression(paramName),
							"Value"
						)
					)
				);

				cmdTry.TryStatements.Add(ifNull);

				// if byref then specify in/out for direction

				if(param.Direction != ParameterDirection.Input)
				{
					CodeAssignStatement assignment = new CodeAssignStatement();

					// xyzParam.Direction

					assignment.Left = new CodePropertyReferenceExpression
					(
						new CodeVariableReferenceExpression(paramName + "Param"), 
						"Direction"
					);

					// ParameterDirection.InOut

					assignment.Right = new CodeFieldReferenceExpression
					(
						new CodeTypeReferenceExpression
						(
							typeof(ParameterDirection)
						), 
						ParameterDirection.InputOutput.ToString()
					);

					cmdTry.TryStatements.Add(assignment);
				}
			}

			ICodeGenerator generator = Provider.CreateGenerator();

			if(generator.Supports(GeneratorSupport.DeclareEvents))
			{
				// Events.FirePreExecute(typeof(<class>), connection, transaction, cmd, typeof(<class>));

				cmdTry.TryStatements.Add(new CodeCommentStatement("Fire PreExecute event to registered handlers"));
				cmdTry.TryStatements.Add(new CodeMethodInvokeExpression(
					new CodePropertyReferenceExpression(null, "Events"),
					"FirePreExecute", 
					new CodeTypeOfExpression(TypeName),
					new CodeArgumentReferenceExpression("connection"),
					new CodeArgumentReferenceExpression("transaction"),
					cmdVariable,
					new CodeTypeOfExpression(TypeName)));
			}

			if(Source.HasResultSet)
			{
				// execute reader

				cmdTry.TryStatements.Add(new CodeCommentStatement("Execute command and extract rows from result set"));
				cmdTry.TryStatements.Add
				(
					new CodeVariableDeclarationStatement
					(
						typeof(IDataReader),
						"reader",
						new CodePrimitiveExpression(null)
					)
				);

				// instantiate an object of type _collectionType for rows

				cmdTry.TryStatements.Add
				(
					new CodeVariableDeclarationStatement
					(
						_collectionType,
						"rows",
						new CodeObjectCreateExpression
						(
							_collectionType,
							new CodeExpression [0]
						)
					)
				);

				CodeVariableReferenceExpression readerVariable = new CodeVariableReferenceExpression("reader");

				// add a new try-finally block for the reader to close reader in all cases

				CodeTryCatchFinallyStatement readerTry = new CodeTryCatchFinallyStatement();
				cmdTry.TryStatements.Add(readerTry);

				// execute reader and assign to readerVariable

				readerTry.TryStatements.Add(new CodeAssignStatement(readerVariable, new CodeMethodInvokeExpression(cmdVariable, "ExecuteReader", 
					new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(CommandBehavior)), CommandBehavior.Default.ToString()))));

				// 2003-05-01, simon.wilson
				// 
				// reader.Close() -> if(reader != null) { reader.Dispose(); }
				//
				// 2003-05-11, simon.wilson
				//
				// reader.Dispose() -> reader.Close() in order that we can access RecordsAffected after closure.

				CodeConditionStatement ifReaderNotNull = new CodeConditionStatement();
				ifReaderNotNull.Condition = new CodeBinaryOperatorExpression(readerVariable, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
				ifReaderNotNull.TrueStatements.Add(new CodeMethodInvokeExpression(readerVariable, "Close"));
				
				readerTry.FinallyStatements.Add(ifReaderNotNull);

				// iterate through reader

				CodeIterationStatement iteration = new CodeIterationStatement();

				iteration.InitStatement = new CodeSnippetStatement("");
				iteration.TestExpression = new CodeMethodInvokeExpression(readerVariable, "Read");
				iteration.IncrementStatement = new CodeSnippetStatement("");

				readerTry.TryStatements.Add(iteration);

				// use factory expression to create object and then add it to the collection

				iteration.Statements.Add
				(
					new CodeMethodInvokeExpression
					(
						new CodeVariableReferenceExpression("rows"),
						"Add",
						FactoryExpression
					)
				);
			}
			else
			{
				// invoke stored procedure without data reader

				cmdTry.TryStatements.Add(new CodeCommentStatement("Execute command. The command does not return a result set"));
				cmdTry.TryStatements.Add(new CodeVariableDeclarationStatement(typeof(int), "rowsAffected", new CodeMethodInvokeExpression(cmdVariable, "ExecuteNonQuery")));
			}

			CodeExpression [] parameters = null;

			if(Source.HasResultSet)
			{
				// Result result = new Result ((int) returnValueParam.Value, reader.RecordsAffected, rows);

				parameters = new CodeExpression []
				{
					new CodeCastExpression(Source.ReturnValue.Type.ClrType,
						new CodePropertyReferenceExpression(returnValueParamVariable, "Value")),
					new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("reader"), "RecordsAffected"),
					new CodeVariableReferenceExpression("rows")
				};
			}
			else
			{
				// Result result = new Result ((int) returnValueParam.Value, rowsAffected);

				parameters = new CodeExpression [] 
				{
					new CodeCastExpression(Source.ReturnValue.Type.ClrType,
						new CodePropertyReferenceExpression(returnValueParamVariable, "Value")),
					new CodeVariableReferenceExpression("rowsAffected")
				};
			}

			cmdTry.TryStatements.Add(new CodeVariableDeclarationStatement(
				"Result", "result", new CodeObjectCreateExpression(ResultTypeName, parameters)));

			if(generator.Supports(GeneratorSupport.DeclareEvents))
			{
				// Events.FirePostExecute(typeof(<class>), connection, transaction, cmd, typeof(<class>), returnValue, rowsAffected, result);
				//
				// do this before extraction of OUTPUT parameters in order that handlers
				// can modify out parameters in the command.

				cmdTry.TryStatements.Add(new CodeCommentStatement("Fire PostExecute event to registered handlers"));
				cmdTry.TryStatements.Add(new CodeMethodInvokeExpression(
					new CodePropertyReferenceExpression(null, "Events"),
					"FirePostExecute", 
					new CodeTypeOfExpression(TypeName),
					new CodeArgumentReferenceExpression("connection"),
					new CodeArgumentReferenceExpression("transaction"),
					cmdVariable,
					new CodeTypeOfExpression(TypeName),
					new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("result"), "ReturnValue"),
					new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("result"), "RowsAffected"),
					new CodeVariableReferenceExpression("result")));
			}

			// assign values to in/out parameters

			foreach(StoredProcedureParameter outParam in Source.Parameters)
			{
				if(outParam.Direction == ParameterDirection.Output || outParam.Direction == ParameterDirection.InputOutput)
				{
					string paramName = outParam.CreateCodeCompatibleName(Provider);

					// if(object.ReferenceEquals(<param>Param.Value, System.DBNull.Value))

					CodeConditionStatement ifNull = new CodeConditionStatement();

					ifNull.Condition = new CodeBinaryOperatorExpression(
						new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(typeof(object)), "ReferenceEquals", 
						new CodePropertyReferenceExpression(new CodeVariableReferenceExpression(paramName + "Param"), "Value"),
						new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(DBNull)), "Value")),
						CodeBinaryOperatorType.ValueEquality,
						new CodePrimitiveExpression(true));

					// <param> = <SqlType>.Null;

					ifNull.TrueStatements.Add
					(
						new CodeAssignStatement
						(
							new CodeArgumentReferenceExpression(paramName),
							new CodeFieldReferenceExpression	
							(
								new CodeTypeReferenceExpression(outParam.Type.ClientType),
								"Null"
							)
						)
					);

					// <param> = new <SqlType>((<ClrType>)<param>Param.Value);

					ifNull.FalseStatements.Add
					(
						new CodeAssignStatement
						(
							new CodeArgumentReferenceExpression(paramName),
							new CodeObjectCreateExpression
							(
								outParam.Type.ClientType,
								new CodeCastExpression
								(
									outParam.Type.ClrType,
									new CodePropertyReferenceExpression
									(
										new CodeVariableReferenceExpression(paramName + "Param"),
										"Value"
									)
								)
							)
						)
					);

					cmdTry.TryStatements.Add(new CodeCommentStatement("Extract OUTPUT value for " + outParam.Name));
					cmdTry.TryStatements.Add(ifNull);
				}
			}

			// add catch block and fire Exception event

			CodeCatchClause catchClause = new CodeCatchClause("e", new CodeTypeReference(typeof(Exception)));

			if(generator.Supports(GeneratorSupport.DeclareEvents))
			{
				// Events.FireException(typeof(<class>), connection, transaction, cmd, typeof(<class>), e);

				catchClause.Statements.Add(new CodeCommentStatement("Fire Exception event to registered handlers"));
				catchClause.Statements.Add(new CodeMethodInvokeExpression(
					new CodePropertyReferenceExpression(null, "Events"),
					"FireException", 
					new CodeTypeOfExpression(TypeName),
					new CodeArgumentReferenceExpression("connection"),
					new CodeArgumentReferenceExpression("transaction"),
					cmdVariable,
					new CodeTypeOfExpression(TypeName),
					new CodeVariableReferenceExpression("e")));
			}

			// throw e;

			catchClause.Statements.Add(new CodeThrowExceptionStatement(new CodeVariableReferenceExpression("e")));
			cmdTry.CatchClauses.Add(catchClause);

			// return result;

			cmdTry.TryStatements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("result")));

			return invokeMethod;
		}

		// ===========================================================================

		CodeMemberMethod GenerateConnectionOverride()
		{
			// delegate to private Invoke method passing in connection and null transaction

			CodeMemberMethod invokeMethod = new CodeMemberMethod();

			invokeMethod.Comments.Add(new CodeCommentStatement("<summary>Invokes [" + Source.Name + "] on a connection which does not have an active local transaction.</summary>", true));
			invokeMethod.ReturnType = new CodeTypeReference("Result");
			invokeMethod.Name = "Invoke";
			invokeMethod.Attributes = MemberAttributes.Static | MemberAttributes.Public;

			invokeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IDbConnection), "connection"));
			AppendParameterList(invokeMethod.Parameters);

			// add RowFactory parameter for delegate-based stored procedures

			CodeExpression [] parameters = new CodeExpression [Source.Parameters.Count + 2];
			int i = 2;

			parameters[0] = new CodeArgumentReferenceExpression("connection");
			parameters[1] = new CodePrimitiveExpression(null);

			foreach(StoredProcedureParameter param in Source.Parameters)
			{
				string paramName = param.CreateCodeCompatibleName(Provider);

				FieldDirection paramDir = FieldDirection.In;
				CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression(param.Type.ClientType, paramName);

				if(param.Direction == ParameterDirection.InputOutput || param.Direction == ParameterDirection.Output)
				{ 
					// OUTPUT parameters can also be in/out so always use ref

					paramDir = FieldDirection.Ref;
				}

				parameters[i++] = new CodeDirectionExpression(paramDir, new CodeArgumentReferenceExpression(paramName));
			}

			CodeMethodReferenceExpression method = new CodeMethodReferenceExpression();
			method.MethodName = "Invoke";

			CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement(
				new CodeMethodInvokeExpression(method, parameters));

			invokeMethod.Statements.Add(returnStatement);

			return invokeMethod;
		}

		// ===========================================================================

		CodeMemberMethod GenerateTransactionOverride()
		{
			// delegate to private Invoke method passing in connection and null transaction

			CodeMemberMethod invokeMethod = new CodeMemberMethod();

			invokeMethod.Comments.Add(new CodeCommentStatement("<summary>Invokes [" + Source.Name + "] within the specified local transaction.</summary>", true));
			invokeMethod.ReturnType = new CodeTypeReference("Result");
			invokeMethod.Name = "Invoke";
			invokeMethod.Attributes = MemberAttributes.Static | MemberAttributes.Public;

			invokeMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IDbTransaction), "transaction"));
			AppendParameterList(invokeMethod.Parameters);

			// add RowFactory parameter for delegate-based stored procedures

			CodeExpression [] parameters = new CodeExpression [Source.Parameters.Count + 2];
			int i = 2;

			parameters[0] = new CodePropertyReferenceExpression(new CodeArgumentReferenceExpression("transaction"), "Connection");
			parameters[1] = new CodeArgumentReferenceExpression("transaction");

			foreach(StoredProcedureParameter param in Source.Parameters)
			{
				string paramName = param.CreateCodeCompatibleName(Provider);

				FieldDirection paramDir = FieldDirection.In;
				CodeParameterDeclarationExpression paramDecl = new CodeParameterDeclarationExpression(param.Type.ClientType, paramName);

				if(param.Direction == ParameterDirection.InputOutput || param.Direction == ParameterDirection.Output)
				{ 
					// OUTPUT parameters can also be in/out so always use ref

					paramDir = FieldDirection.Ref;
				}

				parameters[i++] = new CodeDirectionExpression(paramDir, new CodeArgumentReferenceExpression(paramName));
			}

			CodeMethodReferenceExpression method = new CodeMethodReferenceExpression();
			method.MethodName = "Invoke";

			CodeMethodReturnStatement returnStatement = new CodeMethodReturnStatement(
				new CodeMethodInvokeExpression(method, parameters));

			invokeMethod.Statements.Add(returnStatement);

			return invokeMethod;
		}

		// ===========================================================================

		bool IsResultTypeNested
		{
			get
			{
				ICodeGenerator generator = Provider.CreateGenerator();
				return generator.Supports(GeneratorSupport.NestedTypes);
			}
		}

		// ===========================================================================

		string ResultTypeName
		{
			get
			{
				if(IsResultTypeNested)
					return "Result";
				else
					return TypeName + "Result";
			}
		}

		// ===========================================================================

		void AddEvents(CodeTypeMemberCollection members)
		{
			// private static StoredProcedureEvents _events = new StoredProcedureEvents();

			CodeMemberField field = new CodeMemberField("StoredProcedureEvents", "_events");

			field.Attributes = MemberAttributes.Private | MemberAttributes.Static;
			field.InitExpression = new CodeObjectCreateExpression("StoredProcedureEvents", new CodeExpression [0] {} );

			members.Add(field);

			// public static StoredProcedureEvents Events { get { return _events; } }

			CodeMemberProperty prop = new CodeMemberProperty();

			prop.Name = "Events";
			prop.Comments.Add(new CodeCommentStatement("<summary>The events supported by the stored procedure</summary>", true));
			prop.Attributes = MemberAttributes.Public | MemberAttributes.Static;
			prop.Type = new CodeTypeReference("StoredProcedureEvents");
			prop.GetStatements.Add(new CodeMethodReturnStatement(
				new CodeFieldReferenceExpression(null, "_events")));

			members.Add(prop);
		}
	}
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions