XML Serialization Code Sample

Problem
One is writing code an one needs to serialize an object.

Solution
One can use XML-based serialization, (as long as the object is xml-serializable), as shown below.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.Xml.XPath;
using System.Xml.Xsl;

namespace Test.Framework.Common.Core
{
	/// <summary>
	/// This is a basic serialization helper.
	/// </summary>
	/// <remarks>
	/// Reference...
	/// http://www.dotnetfunda.com/articles/show/articleShow.aspx?aid=98
	/// </remarks>
	public class SerializationHelper
	{
		#region ConstructorMethods

		public SerializationHelper()
		{

		}

		#endregion //ConstructorMethods

		#region HelperMethods

		/// <summary>
		/// This will deserialize any object that can use xml-based serialization.
		/// </summary>
		/// <param name="targetObject">This is the XML to deserialize.</param>
		/// <param name="targetType">This is type to use for the output.</param>
		/// <returns>This is the object deserialized.</returns>
		/// <remarks>
		/// Note the following RTE message that happens when trying to serialize a DataView...
		/// "To be XML serializable, types which inherit from ICollection must have an implementation of 
		/// Add(System.Data.DataRowView) at all levels of their inheritance hierarchy. 
		/// System.Data.DataView does not implement Add(System.Data.DataRowView)".
		/// </remarks>
		public object DeSerializeAnObject(string xmlOfAnObject, System.Type targetType)
		{
			object myObject = null;

			System.Xml.XmlReader myXmlReader = null;
			System.IO.StringReader myStringReader = null;

			try
			{
				myStringReader = new StringReader(xmlOfAnObject);

				System.Xml.Serialization.XmlSerializer myXmlSerializer =
					new System.Xml.Serialization.XmlSerializer(targetType);

				myXmlReader = new XmlTextReader(myStringReader);
				myObject = new object();
				myObject = (object)myXmlSerializer.Deserialize(myXmlReader);
			}
			catch (Exception ex)
			{
				throw new System.ApplicationException(
					"Exception caught and thrown. ex.ToString()='" + ex.ToString() + "'.");
			}
			finally
			{
				if (myXmlReader != null)
				{
					myXmlReader.Close();
				}

				if (myStringReader != null)
				{
					myStringReader.Close();
					myStringReader.Dispose();
				}
			}

			return myObject;
		}

		/// <summary>
		/// This will serialize any object that can use xml-based serialization.
		/// </summary>
		/// <param name="targetObject">This is the object to serialize.</param>
		/// <returns>This is the object serialized as XML.</returns>
		/// <remarks>
		/// Note the following RTE message that happens when trying to serialize a DataView...
		/// "To be XML serializable, types which inherit from ICollection must have an implementation of 
		/// Add(System.Data.DataRowView) at all levels of their inheritance hierarchy. 
		/// System.Data.DataView does not implement Add(System.Data.DataRowView)".
		/// </remarks>
		public string SerializeObject(object targetObject)
		{
			string myXml = "";

			if (targetObject == null)
			{
				throw new System.ApplicationException("The given object, targetObject, is null.");
			}

			System.IO.MemoryStream myMemoryStream = null;

			try
			{
				myMemoryStream = new System.IO.MemoryStream();

				System.Xml.Serialization.XmlSerializer mySerializer =
					new System.Xml.Serialization.XmlSerializer(targetObject.GetType());

				mySerializer.Serialize(myMemoryStream, targetObject);
				myMemoryStream.Position = 0;
				System.Xml.XmlDocument myXmlDoc = new XmlDocument();
				myXmlDoc.Load(myMemoryStream);
				myXml = myXmlDoc.InnerXml;
			}
			catch (Exception ex)
			{
				throw new System.ApplicationException(
					"Exception caught and thrown. ex.ToString()='" + ex.ToString() + "'.");
			}
			finally
			{
				if (myMemoryStream != null)
				{
					myMemoryStream.Close();
					myMemoryStream.Dispose();
				}
			}

			return myXml;
		}

		#endregion //HelperMethods
	}
}

HTH.

Thank you.

— Mark Kamoski

Advertisements

interface for web service without a web reference

Purpose…

Sometimes one has a project where one must dynamically determine which web service to call.

In a typical VS.NET web site project, a web reference is needed to call such a service– but that web reference, when added in the standard fashion, points to a specific URL (web service) and it the URL for that service is not assigned dynamically.

Well, if one needs to determine the URL for the service to call at run-time, and if a typcial web reference cannot do that, then one must call the web service without a web reference.

It is possible to dynamically get a reference to a service, but how do you know the API for that service?

Simple, make sure the service implements a generic interface.

Voila.

One good answer to this is below.

(Make sure you get the latest version available.)

Version 1…

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Test.Framework.Interfaces.BusinessLayer.BusinessEntities
{
	/// <summary>
	/// This is a shared interface for web services that must conform to a standard API and be called without a web reference.
	/// </summary>
	/// <remarks>
	/// Note that the targetIdList parameters are XML, not delimited lists, so data type can be specified if necessary.
	/// Note that this interface is designed for Fire-And-Forget operations, except where a return is absolutely required.
	/// Note that each method has a unique name to simplify attribute creation in the implementing code.
	/// Note that details concerning how and why one might call a web service in this way are found at the following link...
	/// http://www.codeproject.com/KB/cpp/CallWebServicesDynamic.aspx
	/// </remarks>
	public interface IGenericWebServiceStub
	{
		#region InterfaceMethods

		/// <summary>
		/// This saves the given objects.
		/// </summary>
		/// <param name="targetObjectList">This is a list of zero-to-many objects, such as a DataSet serialized as XML.</param>
		void Create(string targetObjectList);

		/// <summary>
		/// This deletes each object that has an ID in the given list.
		/// </summary>
		/// <param name="targetIdList">This is a list of IDs, such as a DataSet serialized as XML.</param>
		void Delete(string targetIdList);

		/// <summary>
		/// This initializes a new object and returns it, without having saved the object.
		/// </summary>
		/// <returns>This is a list containing exactly-one object, such as a DataSet serialized as XML.</returns>
		string Initialize();

		/// <summary>
		/// This retrieves each object that has an ID in the given list.
		/// </summary>
		/// <param name="targetIdList">This is a list of IDs, such as a DataSet serialized as XML.</param>
		/// <returns>This is a list of zero-to-many objects, such as a DataSet serialized as XML.</returns>
		string Retrieve(string targetIdList);

		/// <summary>
		/// This retrieves all objects.
		/// </summary>
		/// <returns>This is a list of zero-to-many objects, such as a DataSet serialized as XML.</returns>
		string RetrieveAll();

		/// <summary>
		/// This retrieves the count of existing objects.
		/// </summary>
		/// <returns>This is the count of objects in the data store.</returns>
		long RetrieveCount();

		/// <summary>
		/// This executes a run-operation.
		/// </summary>
		void Run();

		/// <summary>
		/// This updates the given objects.
		/// </summary>
		/// <param name="targetObjectList">This is a list of zero-to-many objects, such as a DataSet serialized as XML.</param>
		void Update(string targetObjectList);

		#endregion //InterfaceMethods
	}
}

Version 2…

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Test.Framework.Interfaces.BusinessLayer.BusinessEntities
{
	/// <summary>
	/// This is a generic interface for services called without a web reference.
	/// </summary>
	/// <remarks>
	/// Note that this interface is designed for mainly Fire-And-Forget operations.
	/// Note that each method has a single parameter, to ensure uniform call sites.
	/// Note one implementation takes the same a DataSet serialized as XML as a DTO.
	/// Note one implementation uses BusinessLayer-generated, single-column, Guid, PkIds.
	/// Note details concerning how and why one might call a web service as such here...
	/// http://www.codeproject.com/KB/cpp/CallWebServicesDynamic.aspx
	/// </remarks>
	public interface IGenericService
	{
		/// <summary>
		/// This saves the given objects.
		/// </summary>
		/// <param name="targetData">This is a list of zero-to-many objects.</param>
		void Create(string targetData);

		/// <summary>
		/// This deletes each object that has an ID in the given list.
		/// </summary>
		/// <param name="targetData">This is a list of zero-to-many objects, perhaps with just IDs.</param>
		void Delete(string targetData);

		/// <summary>
		/// This initializes a new object, gives it a PkId, but does not save it.
		/// </summary>
		/// <param name="targetData">This is the data to use, typically not necessary.</param>
		/// <returns>This is a list containing exactly-one object.</returns>
		string Initialize(string targetData);

		/// <summary>
		/// This retrieves zero-to-many objects based on the given data.
		/// </summary>
		/// <param name="targetData">This is a list of zero-to-many objects, perhaps with just IDs.</param>
		/// <returns>This is a list of zero-to-many objects.</returns>
		string Retrieve(string targetData);

		/// <summary>
		/// This updates the given objects.
		/// </summary>
		/// <param name="targetData">This is a list of zero-to-many objects.</param>
		void Update(string targetData);
	}
}

HTH.

Thank you.

— Mark Kamoski

Convert From DataSet To Csv

Purpose

The purpose of this post is to show one way to convert a simple DataSet into CSV format.

Problem

Sometimes, one needs to get a DataSet into CSV, such as for exporting to Excel.

Design

Make something that can take a DataSet (or a DataTable) and return a CSV.

Solution

I created some simple helper methods, with Brute-Force looping, and best-effort CSV conformance.

/// <summary>
/// This will convert the given DataTable to a Csv.
/// </summary>
/// <param name="targetData">This is the data to convert.</param>
/// <returns>This is a Csv list.</returns>
public static string ConvertToCsv(DataTable targetData)
{
	string myCsv = "";

	if (targetData == null)
	{
		throw new System.ApplicationException("The given object, targetData, is null.");
	}

	DataTable myDataTable = targetData.Copy();
	DataSet myDataSet = new DataSet();
	myDataSet.Tables.Add(myDataTable);

	//Call a helper.
	myCsv = Team.Framework.Common.Core.Utility.ConvertToCsv(myDataSet);

	return myCsv;
}

/// <summary>
/// This will convert the given DataSet to a Csv.
/// </summary>
/// <param name="targetData">This is the DataSet to convert, at most 1 DataTable.</param>
/// <returns>This is a Csv list.</returns>
public static string ConvertToCsv(DataSet targetData)
{
	string myCsv = "";

	if (targetData == null)
	{
		throw new System.ApplicationException("The given object, targetData, is null.");
	}

	if (targetData.Tables == null)
	{
		throw new System.ApplicationException("The given object, targetData.Tables, is null.");
	}

	const int DefaultRequiredTableCount = 1;

	if (targetData.Tables.Count != DefaultRequiredTableCount)
	{
		throw new System.ApplicationException("ActualTableCount='" + targetData.Tables.Count.ToString() + 
			"' must equal RequiredTableCount='" + DefaultRequiredTableCount.ToString() + "'.");
	}

	if (targetData.Tables[0] == null)
	{
		throw new System.ApplicationException("The given object, targetData.Tables[0], is null.");
	}

	if (targetData.Tables[0].Columns == null)
	{
		throw new System.ApplicationException("The given object, targetData.Tables[0].Columns, is null.");
	}

	if (targetData.Tables[0].Rows == null)
	{
		throw new System.ApplicationException("The given object, targetData.Tables[0].Rows, is null.");
	}

	//Get a helper.
	StringBuilder myBuilder = new StringBuilder();

	//Add a row in the Csv that contains the columns-names from the DataTable.

	bool isFirst1 = true;

	foreach (DataColumn myColumnTemp1 in targetData.Tables[0].Columns)
	{
		string myValueTemp1 = "";

		if (isFirst1)
		{
			isFirst1 = false;
		}
		else
		{
			myBuilder.Append(",");
		}

		myBuilder.Append("\"");
		myValueTemp1 = myColumnTemp1.ColumnName;
		myValueTemp1 = myValueTemp1.Replace("\"", "\"\"");
		myBuilder.Append(myColumnTemp1.ColumnName);
		myBuilder.Append("\"");
	}

	//Add a row in the Csv for each row in the DataTable.

	foreach (DataRow myRowTemp in targetData.Tables[0].Rows)
	{
		myBuilder.Append(Environment.NewLine);
		bool isFirst2 = true;

		foreach (DataColumn myColumnTemp2 in targetData.Tables[0].Columns)
		{
			string myValueTemp2 = "";

			if (isFirst2)
			{
				isFirst2 = false;
			}
			else
			{
				myBuilder.Append(",");
			}

			myBuilder.Append("\"");

			if ((myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(bool)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(byte)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(char)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(decimal)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(double)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(float)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(int)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(long)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(sbyte)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(short)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(string)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(uint)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(ulong)) ||
				(myRowTemp[myColumnTemp2.ColumnName].GetType() == typeof(ushort)))
			{
				myValueTemp2 = myRowTemp[myColumnTemp2.ColumnName].ToString();
				myValueTemp2 = myValueTemp2.Replace("\"", "\"\"");
			}
			else
			{
				myValueTemp2 = "";
			}

			myBuilder.Append(myValueTemp2);
			myBuilder.Append("\"");
		}
	}

	myCsv = myBuilder.ToString();

	return myCsv;
}

HTH.

Thank you.

— Mark Kamoski

DotNet Export To Word Or Html Code Sample

This is the helper class…


using System;
using System.Text;
using System.Web;

namespace Test.Framework.Common.Core
{
	/// 
	/// This is global utility class that helps with some common Word-based functionality.
	/// 
	public sealed class WordHelper
	{
		#region MemberConsts

		public const int InnerHtmlLengthMin = 0;
		public static readonly int InnerHtmlLengthMax = int.MaxValue;
		public const double DefaultDouble = double.MinValue;
		public const Test.Framework.Common.Core.WordHelper.FileExtension DefaultFileExtension = Test.Framework.Common.Core.WordHelper.FileExtension.DOC;
		public const Test.Framework.Common.Core.WordHelper.PageOrientation DefaultPageOrientation = Test.Framework.Common.Core.WordHelper.PageOrientation.Portrait;
		public const Test.Framework.Common.Core.WordHelper.PageMarginInInches DefaultPageMarginInInches = Test.Framework.Common.Core.WordHelper.PageMarginInInches._0_POINT_5_;

		#endregion //MemberConsts

		#region MemberEnums

		public enum FileExtension
		{
			HTM,
			DOC
		}

		public enum PageOrientation
		{
			Landscape,
			Portrait
		}

		public enum PageMarginInInches
		{
			_0_POINT_5_,
			_1_POINT_0_,
			_1_POINT_5_,
			_2_POINT_0_
		}

		#endregion //MemberEnums

		#region ConversionHelpers

		public static Test.Framework.Common.Core.WordHelper.PageOrientation ConvertToPageOrientationByForce(string targetOrientation)
		{
			Test.Framework.Common.Core.WordHelper.PageOrientation myOrientation = Test.Framework.Common.Core.WordHelper.DefaultPageOrientation;

			try
			{
				targetOrientation = (targetOrientation + "").Trim().ToLower();

				if (targetOrientation == Test.Framework.Common.Core.WordHelper.PageOrientation.Landscape.ToString().ToLower())
				{
					myOrientation = Test.Framework.Common.Core.WordHelper.PageOrientation.Landscape;
				}
				else if (targetOrientation == Test.Framework.Common.Core.WordHelper.PageOrientation.Portrait.ToString().ToLower())
				{
					myOrientation = Test.Framework.Common.Core.WordHelper.PageOrientation.Portrait;
				}
				else
				{
					myOrientation = Test.Framework.Common.Core.WordHelper.DefaultPageOrientation;
				}
			}
			catch 
			{
				myOrientation = Test.Framework.Common.Core.WordHelper.DefaultPageOrientation;
			}

			return myOrientation;
		}

		public static Test.Framework.Common.Core.WordHelper.PageMarginInInches ConvertToPageMarginInInchesByForce(string targetPageMarginInInches)
		{
			Test.Framework.Common.Core.WordHelper.PageMarginInInches myPageMarginInInches = Test.Framework.Common.Core.WordHelper.DefaultPageMarginInInches;

			try
			{
				targetPageMarginInInches = (targetPageMarginInInches + "").Trim().ToLower();
				double myPageMarginInInchesAsDouble = Test.Framework.Common.Core.WordHelper.ConvertToDoubleByForce(targetPageMarginInInches);

				if (myPageMarginInInchesAsDouble == 
					Test.Framework.Common.Core.WordHelper.ConvertToDoubleByForce(Test.Framework.Common.Core.WordHelper.PageMarginInInches._0_POINT_5_))
				{
					myPageMarginInInches = Test.Framework.Common.Core.WordHelper.PageMarginInInches._0_POINT_5_;
				}
				else if (myPageMarginInInchesAsDouble == 
					Test.Framework.Common.Core.WordHelper.ConvertToDoubleByForce(Test.Framework.Common.Core.WordHelper.PageMarginInInches._1_POINT_0_))
				{
					myPageMarginInInches = Test.Framework.Common.Core.WordHelper.PageMarginInInches._1_POINT_0_;
				}
				else if (myPageMarginInInchesAsDouble == 
					Test.Framework.Common.Core.WordHelper.ConvertToDoubleByForce(Test.Framework.Common.Core.WordHelper.PageMarginInInches._1_POINT_5_))
				{
					myPageMarginInInches = Test.Framework.Common.Core.WordHelper.PageMarginInInches._1_POINT_5_;
				}
				else if (myPageMarginInInchesAsDouble == 
					Test.Framework.Common.Core.WordHelper.ConvertToDoubleByForce(Test.Framework.Common.Core.WordHelper.PageMarginInInches._2_POINT_0_))
				{
					myPageMarginInInches = Test.Framework.Common.Core.WordHelper.PageMarginInInches._2_POINT_0_;
				}
				else
				{
					myPageMarginInInches = Test.Framework.Common.Core.WordHelper.DefaultPageMarginInInches;
				}
			}
			catch
			{
				myPageMarginInInches = Test.Framework.Common.Core.WordHelper.DefaultPageMarginInInches;
			}

			return myPageMarginInInches;
		}

		public static double ConvertToDoubleByForce(Test.Framework.Common.Core.WordHelper.PageMarginInInches targetValue)
		{
			double myDouble = Test.Framework.Common.Core.WordHelper.DefaultDouble;

			try
			{
				string myInputAsString = targetValue.ToString().ToLower();
				myInputAsString = myInputAsString.Replace("point", ".");
				myInputAsString = myInputAsString.Replace("_", "");

				//Call a helper.
				myDouble = Test.Framework.Common.Core.WordHelper.ConvertToDoubleByForce(myInputAsString);
			}
			catch
			{
				myDouble = Test.Framework.Common.Core.WordHelper.DefaultDouble;
			}

			return myDouble;
		}

		public static double ConvertToDoubleByForce(string targetValue)
		{
			double myDouble = Test.Framework.Common.Core.WordHelper.DefaultDouble;

			try
			{
				targetValue = (targetValue + "").Trim().ToLower();
				myDouble = Convert.ToDouble(targetValue);
			}
			catch
			{
				myDouble = Test.Framework.Common.Core.WordHelper.DefaultDouble;
			}

			return myDouble;
		}

		#endregion //ConversionHelpers

		#region Helpers

		public static bool IsDouble(string targetText)
		{
			bool isDoubleFlag = false;

			try
			{
				targetText = (targetText + "").Trim().ToLower();
				double myTemp = Convert.ToDouble(targetText);
				isDoubleFlag = true;
			}
			catch
			{
				isDoubleFlag = false;
			}

			return isDoubleFlag;
		}

		private static string GetHtmlPrefix(WordHelper.PageOrientation targetOrientation, WordHelper.PageMarginInInches pageMargins)
		{
			string myPrefix = "";

			StringBuilder myBuilder = new StringBuilder();

			myBuilder.AppendLine(" ");

			myBuilder.AppendLine(" ");
			myBuilder.AppendLine("EXPORT ");
			myBuilder.AppendLine(@" ");
			myBuilder.AppendLine(@" ");
			myBuilder.AppendLine(" ");
			myBuilder.AppendLine(" ");

			myBuilder.AppendLine(" ");
			myBuilder.AppendLine("@page Section1 ");
			myBuilder.AppendLine("{");

			if (targetOrientation == PageOrientation.Portrait)
			{
				myBuilder.AppendLine("    size:8.5in 11.0in;");
			}
			else
			{
				myBuilder.AppendLine("    size:11.0in 8.5in;");
			}

			myBuilder.AppendLine("    mso-page-orientation:" + targetOrientation.ToString() + ";");

			switch (pageMargins)
			{
				case Test.Framework.Common.Core.WordHelper.PageMarginInInches._0_POINT_5_:
					myBuilder.AppendLine("    margin:0.5in 0.5in 0.5in 0.5in;");
					break;
				case Test.Framework.Common.Core.WordHelper.PageMarginInInches._1_POINT_0_:
					myBuilder.AppendLine("    margin:1.0in 1.0in 1.0in 1.0in;");
					break;
				case Test.Framework.Common.Core.WordHelper.PageMarginInInches._1_POINT_5_:
					myBuilder.AppendLine("    margin:1.5in 1.5in 1.5in 1.5in;");
					break;
				case Test.Framework.Common.Core.WordHelper.PageMarginInInches._2_POINT_0_:
					myBuilder.AppendLine("    margin:2.0in 2.0in 2.0in 2.0in;");
					break;
				default:
					myBuilder.AppendLine("    margin:1.0in 1.0in 1.0in 1.0in;");
					break;
			}

			myBuilder.AppendLine("} ");

			myBuilder.AppendLine("div.Section1 ");
			myBuilder.AppendLine("{ ");
			myBuilder.AppendLine("    page: Section1; ");
			myBuilder.AppendLine("} ");

			myBuilder.AppendLine(" ");
			myBuilder.AppendLine(" " );
			myBuilder.AppendLine(" " );
			myBuilder.AppendLine("<div class='Section1'> " );

			myPrefix = myBuilder.ToString();

			return myPrefix;
		}

		public static string GetHtmlBodyCleaned(string targetBody)
		{
			string myBodyCleaned = "";

			myBodyCleaned = (targetBody + "").Trim();

			myBodyCleaned.Replace(@"<br />", @"<br />");
			myBodyCleaned.Replace(@"<br />", @"<br />");
			myBodyCleaned.Replace(@"<p>", @"</p><p>");
			myBodyCleaned.Replace(@"</p>", @"");

			return myBodyCleaned;
		}

		public static string GetHtmlSuffix()
		{
			string mySuffix = "";

			StringBuilder myBuilder = new StringBuilder();
			myBuilder.AppendLine("</div> ");
			myBuilder.AppendLine(" " );
			myBuilder.AppendLine(" ");
			mySuffix = myBuilder.ToString();

			return mySuffix;
		}

		/// 
		/// This will set the client-script-block on the target page.
		/// 
		/// This is the page that contains the data to be exported.
		/// This is the name to use for the client script function.
		/// This is any string that idenfies this script block uniquely on the page.
		/// This is the ClientID of the DIV tag that holds the data that is being exported.
		/// This is the ID of the INPUT tag that holds the InnerHtml that is being exported.
		public static void SetClientScriptBlock(System.Web.UI.Page targetPage, string targetScriptFunctionName, string targetScriptKey, string targetDivTagClientId, string targetInputTagClientId)
		{
			//Validate the given Page.

			if (targetPage == null)
			{
				throw new System.ApplicationException("The given object, targetPage, is null.");
			}

			//Validate the given key for the script-block.

			targetScriptKey = (targetScriptKey + "").Trim();

			if (targetScriptKey.Length <= 0)
			{
				throw new System.ApplicationException("The value targetScriptKey.Length.ToString()='" + targetScriptKey.Length.ToString() + "' is not valid.");
			}

			//Validate the given function name.

			targetScriptFunctionName = (targetScriptFunctionName + "").Trim();

			if (targetScriptFunctionName.Length <= 0)
			{
				throw new System.ApplicationException("The value targetScriptFunctionName.Length.ToString()='" + targetScriptFunctionName.Length.ToString() + "' is not valid.");
			}

			//Validate the given ID for the DivTag.

			targetDivTagClientId = (targetDivTagClientId + "").Trim();

			if (targetDivTagClientId.Length <= 0)
			{
				throw new System.ApplicationException("The value targetDivTagClientId.Length.ToString()='" + targetDivTagClientId.Length.ToString() + "' is not valid.");
			}

			//Validate the given ID for the InputTag.

			targetInputTagClientId = (targetInputTagClientId + "").Trim();

			if (targetInputTagClientId.Length <= 0)
			{
				throw new System.ApplicationException("The value targetInputTagClientId.Length.ToString()='" + targetInputTagClientId.Length.ToString() + "' is not valid.");
			}

			Type myTypeToUse = targetPage.GetType();

			if (!targetPage.ClientScript.IsClientScriptBlockRegistered(myTypeToUse, targetScriptKey))
			{
				string myScriptCode = Test.Framework.Common.Core.WordHelper.GetClientScript(targetScriptFunctionName, targetDivTagClientId, targetInputTagClientId);
				const bool addScriptTagsFlag = false;
				targetPage.ClientScript.RegisterClientScriptBlock(myTypeToUse, targetScriptKey, myScriptCode, addScriptTagsFlag);
			}
		}

		/// 
		/// This will get the client-script-block that is required for the call.
		/// 
		/// This is the name to use for the client script function.
		/// This is the ID of the DIV tag that holds the data that is being exported.
		/// This is the ID of the INPUT tag that holds the InnerHtml that is being exported.
		/// This is the client script.
		public static string GetClientScript(string targetScriptFunctionName, string targetDivTagClientId, string targetInputTagClientId)
		{
			string myScript = "";

			//Validate the given function name.

			targetScriptFunctionName = (targetScriptFunctionName + "").Trim();

			if (targetScriptFunctionName.Length <= 0)
			{
				throw new System.ApplicationException("The value targetScriptFunctionName.Length.ToString()='" + targetScriptFunctionName.Length.ToString() + "' is not valid.");
			}

			//Validate the ID for the DivTag.

			targetDivTagClientId = (targetDivTagClientId + "").Trim();

			if (targetDivTagClientId.Length <= 0)
			{
				throw new System.ApplicationException("The value targetDivTagClientId.Length.ToString()='" + targetDivTagClientId.Length.ToString() + "' is not valid.");
			}

			//Validate the ID for the InputTag.

			targetInputTagClientId = (targetInputTagClientId + "").Trim();

			if (targetInputTagClientId.Length <= 0)
			{
				throw new System.ApplicationException("The value targetInputTagClientId.Length.ToString()='" + targetInputTagClientId.Length.ToString() + "' is not valid.");
			}

			//Get a worker object.
			StringBuilder myBuilder = new StringBuilder();

			//Build the text.

			myBuilder.AppendLine(" ");
			myBuilder.AppendLine("  ");
			myBuilder.AppendLine(" function " + targetScriptFunctionName + "() ");
			myBuilder.AppendLine(" { ");
			myBuilder.AppendLine("     //Get the element DIV that contains the GridView that contains the data. ");
			myBuilder.AppendLine("     var myElementSource = document.getElementById('" + targetDivTagClientId + "'); ");
			myBuilder.AppendLine("     //Get a handle to the element that is the hidden field that will hold the data for export. ");
			myBuilder.AppendLine("     var myElementDestination = document.getElementById('" + targetInputTagClientId + "'); ");
			myBuilder.AppendLine("     //Copy from the the source to the destination. ");
			myBuilder.AppendLine("     myElementDestination.value = myElementSource.innerHTML; ");
			myBuilder.AppendLine(" } ");
			myBuilder.AppendLine("  ");
			myBuilder.AppendLine(" ");

			//Get the text.
			myScript = myBuilder.ToString();

			return myScript;
		}

		public static void ExportToWord(
			System.Web.UI.Page targetPage, 
			string innerHtmlFromDiv, 
			string targetOrientation, 
			string targetMarginInInches, 
			Test.Framework.Common.Core.WordHelper.FileExtension targetExtension)
		{
			Test.Framework.Common.Core.WordHelper.PageOrientation myOrientation = Test.Framework.Common.Core.WordHelper.ConvertToPageOrientationByForce(targetOrientation);
			Test.Framework.Common.Core.WordHelper.PageMarginInInches myMarginInInches = Test.Framework.Common.Core.WordHelper.ConvertToPageMarginInInchesByForce(targetMarginInInches);

			//Call a helper.
			Test.Framework.Common.Core.WordHelper.ExportToWord(targetPage, innerHtmlFromDiv, myOrientation, myMarginInInches, targetExtension);
		}

		/// 
		/// This will export the given innerHtml to a single-worksheet, Word-friendly, HTML file.
		/// 
		/// This is the current page.
		/// This is the InnerHtml from a DIV that holds the data to be exported.
		/// This is the orientation for the output.
		/// This is the margin-size for the output.
		/// This is the extension for the output.
		public static void ExportToWord(
			System.Web.UI.Page targetPage, 
			string innerHtmlFromDiv, 
			Test.Framework.Common.Core.WordHelper.PageOrientation targetOrientation, 
			Test.Framework.Common.Core.WordHelper.PageMarginInInches targetMarginInInches, 
			Test.Framework.Common.Core.WordHelper.FileExtension targetExtension)
		{
			//NOTE: 20080925. This may work with other HTML passed to it (not just InnerHtml from a DIV) but so far it has only been tested with InnerHtml from a DIV.

			//Validate the given Page.
			if (targetPage == null)
			{
				throw new System.ApplicationException("The given object, targetPage, is null.");
			}

			//Validate the given Response.
			if (targetPage.Response == null)
			{
				throw new System.ApplicationException("The given object, targetPage.Response, is null.");
			}

			HttpResponse myTargetResponse = targetPage.Response;

			//Validate the given InnnerHtml.
			innerHtmlFromDiv = (innerHtmlFromDiv + "").Trim();

			if (innerHtmlFromDiv.Length  Test.Framework.Common.Core.WordHelper.InnerHtmlLengthMax)
			{
				throw new System.ApplicationException(
					"The given value innerHtmlFromDiv.Length.ToString()='" + innerHtmlFromDiv.Length.ToString() + 
					"' is not valid because it must be less-than-or-equal-to InnerHtmlLengthMax='" + Test.Framework.Common.Core.WordHelper.InnerHtmlLengthMax.ToString() + "'.");
			}

			//Get a working builder.
			StringBuilder myBuilder = new StringBuilder();

			//Call a helper to get the standard prefix that is written when Word 2003 saves from ".XLS" to ".HTM".

			string myHtmlPrefix = "";
			myHtmlPrefix = Test.Framework.Common.Core.WordHelper.GetHtmlPrefix(targetOrientation, targetMarginInInches);

			//Add the prefix.
			myBuilder.Append(myHtmlPrefix);

			//Get the basline HTML to be exported.
			string myHtmlBody = innerHtmlFromDiv;

			myHtmlBody = Test.Framework.Common.Core.WordHelper.GetHtmlBodyCleaned(myHtmlBody);

			//Add the body.
			myBuilder.Append(myHtmlBody);

			//Call a helper to get the standard suffix that is written when Word 2003 saves from ".XLS" to ".HTM".
			string myHtmlSuffix = Test.Framework.Common.Core.WordHelper.GetHtmlSuffix();

			//Add the suffix.
			myBuilder.Append(myHtmlSuffix);

			//Get a unique suffix for the output file.
			string DateTimeStringSuffix = DateTime.Now.ToString("yyyyMMddHHMMssfffffff");

			//Prepare the response.
			myTargetResponse.Clear();
			myTargetResponse.ClearHeaders();
			myTargetResponse.AddHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0");
			myTargetResponse.AddHeader("Content-transfer-encoding", "binary");
			myTargetResponse.AddHeader("Pragma", "anytextexeptno-cache, true");
			myTargetResponse.AddHeader("content-disposition", "attachment;filename=EXPORT_" + DateTimeStringSuffix + "." + targetExtension.ToString());
			string myHtmlComplete = myBuilder.ToString();

			//Prepare the page.
			targetPage.EnableViewState = false;

			//Write.
			myTargetResponse.Write(myHtmlComplete);

			//End the process, which must be done outside of all try-catch blocks.
			myTargetResponse.End();
		}

		#endregion //Helpers
	}
}

This is a sample usage code-infront…

<%@ page autoeventwireup="true" codefile="ViewerFormVersion02.aspx.cs" enableeventvalidation="false" inherits="Test.ExportForWord.Web.Components.Forms.Private.Administrator.ViewerFormVersion02" language="C#" maintainscrollpositiononpostback="true" validaterequest="false" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
	<title>ViewerForWord</title>
</head>
<body>
	<form id="form1" runat="server">
	<p>
		<asp:literal id="TopStatusLiteral" runat="server"></asp:literal>&nbsp;
	</p>
	<p>
		<asp:hyperlink id="MainHyperlink" runat="server" navigateurl="~/Default.aspx">Back</asp:hyperlink>
		<asp:label id="Label1" runat="server" font-bold="true">&nbsp;|&nbsp;</asp:label>
		<asp:linkbutton id="TopExportButton" runat="server" onclick="ExportButton_Click" text="Export"></asp:linkbutton>
	</p>
	<table border="1" cellpadding="5" cellspacing="0">
		<tr align="left" valign="top">
			<td align="left" valign="top">
				<asp:label id="Label5" runat="server">Export Settings</asp:label>
			</td>
		</tr>
		<tr align="left" valign="top">
			<td align="left" valign="top">
				<asp:label id="Label6" runat="server">Page Orientation</asp:label>
				<asp:radiobuttonlist id="OrientationRadioButtonList" runat="server" repeatcolumns="9" repeatdirection="horizontal" repeatlayout="table">
					<asp:listitem enabled="true" selected="true" text="Portrait" value="Portrait"></asp:listitem>
					<asp:listitem enabled="true" selected="false" text="Landscape" value="Landscape"></asp:listitem>
				</asp:radiobuttonlist>
			</td>
		</tr>
		<tr align="left" valign="top">
			<td align="left" valign="top">
				<asp:label id="Label7" runat="server">Page Margins</asp:label>
				<asp:radiobuttonlist id="MarginRadioButtonList" runat="server" repeatcolumns="9" repeatdirection="horizontal" repeatlayout="Table">
					<asp:listitem enabled="true" selected="true" text="0.5''" value="0.5"></asp:listitem>
					<asp:listitem enabled="true" selected="false" text="1.0''" value="1.0"></asp:listitem>
					<asp:listitem enabled="true" selected="false" text="1.5''" value="1.5"></asp:listitem>
					<asp:listitem enabled="true" selected="false" text="2.0''" value="2.0"></asp:listitem>
				</asp:radiobuttonlist>
			</td>
		</tr>
	</table>
	<p>
		<asp:label id="Label3" runat="server">Row&nbsp;Count:&nbsp;</asp:label><asp:label id="TotalRowCountLabel" runat="server">0</asp:label>
	</p>
	<div id="ExportDiv" runat="server">
		<asp:gridview id="WordGridView" runat="server" allowpaging="false" allowsorting="false" autogeneratecolumns="true" autogeneratedeletebutton="false" autogenerateeditbutton="false" autogenerateselectbutton="false" bordercolor="Silver" borderstyle="Solid" borderwidth="1px" emptydatatext="NULL" enablesortingandpagingcallbacks="false" enabletheming="false" enableviewstate="true" gridlines="both" showfooter="false" showheader="true">
			<headerstyle horizontalalign="left" verticalalign="Top" font-bold="true" />
			<rowstyle horizontalalign="left" verticalalign="Top" />
		</asp:gridview>
	</div>
	<input id="ExportInput" runat="server" type="hidden" value="" />
	<p>
		<asp:hyperlink id="Hyperlink1" runat="server" navigateurl="~/Default.aspx">Back</asp:hyperlink>
		<asp:label id="Label4" runat="server" font-bold="true">&nbsp;|&nbsp;</asp:label>
		<asp:linkbutton id="BottomExportButton" runat="server" onclick="ExportButton_Click" text="Export"></asp:linkbutton>
	</p>
	<p>
		<asp:literal id="BottomStatusLiteral" runat="server"></asp:literal><asp:label id="Label2" runat="server">&nbsp;</asp:label>
	</p>
	</form>
</body>
</html>

This is sample usage code-behind…

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

namespace Test.ExportForWord.Web.Components.Forms.Private.Administrator
{
	/// 
	/// This will display data for Word export.
	/// 
	/// 
	/// Note that this page cannot be a cross-page-post-target because the source page will not display errors properly.
	/// 
	public partial class ViewerFormVersion02 : System.Web.UI.Page
	{
		#region MemberEnums

		/// 
		/// This is mock-data helper.
		/// 
		private enum ColumnName
		{
			Count,
			First,
			Middle,
			Last,
			Email,
		}

		#endregion //MemberEnums

		#region ValidationMethods

		/// 
		/// This is a mock custom validator.
		/// 
		private void ValidateExport()
		{
			//Continue.
		}

		#endregion //ValidationMethods

		#region HelperMethods

		/// 
		/// This is a mock-data helper.
		/// 
		/// This is mock data.
		private DataTable GetData()
		{
			DataTable myDataTable = new DataTable();
			myDataTable.Columns.Add(ColumnName.Count.ToString(), typeof(string));
			myDataTable.Columns.Add(ColumnName.First.ToString(), typeof(string));
			myDataTable.Columns.Add(ColumnName.Last.ToString(), typeof(string));
			myDataTable.Columns.Add(ColumnName.Email.ToString(), typeof(string));
			DataRow myDataRowTemp = null;

			for (int myCount = 1; myCount <= 9; ++myCount)
			{
				myDataRowTemp = myDataTable.NewRow();
				myDataRowTemp[ColumnName.Count.ToString()] = myCount.ToString();
				myDataRowTemp[ColumnName.First.ToString()] = Guid.NewGuid().ToString().Trim().ToUpper();
				myDataRowTemp[ColumnName.Last.ToString()] = Guid.NewGuid().ToString().Trim().ToUpper();
				myDataRowTemp[ColumnName.Email.ToString()] = Guid.NewGuid().ToString().Trim().ToUpper() + "@" + Guid.NewGuid().ToString().Trim().ToUpper() + ".COM";
				myDataTable.Rows.Add(myDataRowTemp);
			}

			myDataTable.AcceptChanges();

			return myDataTable;
		}

		#endregion //HelperMethods

		#region HandlerMethods

		protected void Page_Load(object sender, EventArgs e)
		{
			try
			{
				//Set values for export.
				const string ScriptPrefix = "_d26109b1_f78b_4c79_96a6_c87a892e4761_";
				const string ScriptSuffix = "GetHtmlForWord";
				Test.Framework.Common.Core.WordHelper.SetClientScriptBlock(this.Page, ScriptPrefix + ScriptSuffix, ScriptPrefix, this.ExportDiv.ClientID, this.ExportInput.ClientID);
				this.TopExportButton.OnClientClick = ScriptPrefix + ScriptSuffix + "();";
				this.BottomExportButton.OnClientClick = this.TopExportButton.OnClientClick;

				if (this.IsPostBack)
				{
					//Continue.
				}
				else
				{
					//To get data from Session, do something like this...
					//string myTestSessionData = this.Session[Test.ExportForWord.Common.Core.Enums.SessionKey.TestKeyName.ToString()].ToString();

					//To get data from this page, do something like this...
					DataTable myTableForWord = this.GetData();

					this.TotalRowCountLabel.Text = myTableForWord.Rows.Count.ToString();
					this.WordGridView.DataSource = myTableForWord;
					this.WordGridView.DataBind();
				}
			}
			catch (Exception ex)
			{

#if (DEBUG)
				this.TopStatusLiteral.Text = ex.ToString();
#else
				this.TopStatusLiteral.Text = ex.Message;
#endif

				this.BottomStatusLiteral.Text = this.TopStatusLiteral.Text;
			}
		}

		protected void ExportButton_Click(object sender, EventArgs e)
		{
			try
			{
				//This is optional code, shown in this code sample for demonstration purposed only.
				this.TopStatusLiteral.Text = "";
				this.BottomStatusLiteral.Text = this.TopStatusLiteral.Text;

				//This is optional code, shown in this code sample for demonstration purposed only.
				this.ValidateExport();
			}
			catch (Exception ex)
			{

#if (DEBUG)
				this.TopStatusLiteral.Text = ex.ToString();
#else
				this.TopStatusLiteral.Text = ex.Message;
#endif

				this.BottomStatusLiteral.Text = this.TopStatusLiteral.Text;
			}

			//Call a helper to do the work and do not wrap this call in a try-catch.
			Test.Framework.Common.Core.WordHelper.ExportToWord(
				this.Page,
				this.ExportInput.Value,
				this.OrientationRadioButtonList.SelectedValue,
				this.MarginRadioButtonList.SelectedValue,
				Test.Framework.Common.Core.WordHelper.FileExtension.DOC
				);
		}

		#endregion //HandlerMethods
	}
}

LinqHelper Refactor Idea

Purpose

The purpose of this post is to provide a simple refactoring of Mike Hunter’s most excellent LinqHelper.

Please see the code below.

Please see the orginal post by Mike, linked below.

Code

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using Team;
using Team.Framework;
using Team.Framework.Common;
using Team.Framework.Common.Linq;

namespace Team.Framework.Common.Linq
{
	/// <summary>
	/// This is a utility for using L2s-based objects.
	/// </summary>
	/// <remarks>
	/// This code was borrowed '2009-Aug-11-Tue 08:38:14.1223174 AM -04:00' and it should not be modified manually.
	/// For details, see...
	/// http://complexitykills.blogspot.com/2008/03/disconnected-linq-to-sql-tips-part-1.html 
	/// </remarks>
	public static class LINQHelper
	{
		/// <summary> 
		/// Makes a copy of an existing LINQ to SQL entity and it's children. 
		/// </summary> 
		/// <typeparam name="T"></typeparam> 
		/// <param name="entitySource">The LINQ to SQL entity to copy</param> 
		/// <returns></returns> 
		public static T CopyEntityDeep<T>(T entitySource)
		{
			if (entitySource == null)
				return default(T);
			return (T)DeserializeEntity(SerializeEntity(entitySource), entitySource.GetType());
		}

		/// <summary> 
		/// Makes a copy of a list of existing LINQ to SQL entities and their children. 
		/// </summary> 
		/// <typeparam name="T"></typeparam> 
		/// <param name="source">The LIST of SQL entities to copy 
		/// </param> 
		/// <returns></returns> 
		public static List<T> CopyEntityListDeep<T>(List<T> entitySourceList)
		{
			List<T> result = new List<T>();
			if (entitySourceList == null) return null;
			foreach (T entitySource in entitySourceList)
			{
				T entityTarget = CopyEntityDeep(entitySource);
				result.Add(entityTarget);
			}
			return result;
		}

		public static string SerializeEntity<T>(T entitySource)
		{
			DataContractSerializer dcs = new DataContractSerializer(entitySource.GetType());
			if (entitySource == null) return null; StringBuilder sb = new StringBuilder(); XmlWriter xmlw = XmlWriter.Create(sb); dcs.WriteObject(xmlw, entitySource); xmlw.Close();
			return sb.ToString();
		}

		/// <summary>
		/// This will serialize and optionally convert to "utf-8" format.
		/// </summary>
		/// <typeparam name="T">This is the type.</typeparam>
		/// <param name="entitySource">This is the object to use.</param>
		/// <param name="convertToUtf8">This is the flag for utf conversion.</param>
		/// <returns>This is XML.</returns>
		/// <remarks>
		/// NOTE. 20091202. This was added because the standard call returns "utf-16", which sometimes does not load into VS.NET and IE8.
		/// </remarks>
		public static string SerializeEntity<T>(T entitySource, bool convertToUtf8)
		{
			string myString = "";

			myString = (LINQHelper.SerializeEntity(entitySource) + "").Trim();

			if (convertToUtf8)
			{
				myString = myString.Replace("utf-16", "utf-8");
			}

			return myString;
		}

		public static object DeserializeEntity(string entitySource, Type entityType)
		{
			object entityTarget;
			if (entityType == null) return null;
			DataContractSerializer dcs = new DataContractSerializer(entityType);
			StringReader sr = new StringReader(entitySource); XmlTextReader xmltr = new XmlTextReader(sr); entityTarget = (object)dcs.ReadObject(xmltr); xmltr.Close();
			return entityTarget;
		}
	}
}

HTH.

Thank you.

— Mark Kamoski

T4, T4 ToolBox, Linq To Sql Entity Base, Code Description

 

Download Link —

The code can be downloaded at the following link.

https://mkamoski1.wordpress.com/2009/12/01/t4-t4-toolbox-linq-to-sql-entity-base-code-sample/

Credits —

I want to give special thanks to the following people, without whom this sample would not exist.

This is in no particular order, and certainly not a complete list, please note the following superstars…

Oleg Sych
http://www.olegsych.com/
http://www.codeplex.com/t4toolbox

Matthew Hunter
http://complexitykills.blogspot.com/
http://www.codeplex.com/LINQ2SQLEB/

Kristofer Andersson
http://www.huagati.com/

Frans Bouma
http://weblogs.asp.net/fbouma/
http://www.llblgen.com/

(BTW, one would probably learn more from studying their work rather than mine.)

Purpose —

The purpose of this article is to show one way to use T4 templates and T4ToolBox and LinqToSqlEntityBase and Repository and other nice things to help one code a bit faster.

Warning —

I trt to follow, a bit, ideas from GEFN and RAD and YAGNI and a few other quick-mode development ideas. I try to Agile, especially bold, and I refactor heavily. See this List Of Software Development Philosophies for some general details on all this. However, even in doing so, I only do it loosely. Etc. As such, my style is a bit loose and it may not be for everyone. Also, please note that this is a work in progress. APIs may change. Interfaces may change. I have the luxury of working on my own projects from start-to-finish, so I have no compunction in making big changes if necessary. I do, however, have had this code in production as of 2009-Oct-01 and it will probably remain in production until about 2010-Apr-01, when VS.NET 2010 is released and I get it and I implement Self-Tracking-Entities and etc.

Problem —

Linq To Sql was bothering me. I wanted to use Linq-To-Sql in a disconnected way. I did not like the way that the DataContext was getting littered about my layers. I did not like the way that Linq entities had to have reference to an active DataContext object in some scenarios. I did not like the way the baseline CRUD functionality required using some methods of the DataContext and some Entity static methods. Etc. I wanted a Repository pattern, or a quasi-one. I wanted a “Manager” that could “do all the work” and an Entity that could “hold all the data”. I wanted to hand an Entity to my Manager and say “Create” or “Update”. Etc.

Writing the same code was bothering me. I had CRUD methods that I wanted to be generated rather than write by hand. I wanted to use code generation. It did not look like I would be able to get a license to CodeSmith at my current gig. The MyGeneration project, an old favorite, seemed a bit stale. The available code generators were WAY too much for me. I wanted something simple. The generator that I wrote myself was pretty ugly and it was not powerful enough. I did not want a black-box generator. I wanted to generate code that could, if necessary,  have the code generation engine unplugged and the code should be human-readable and not too far from what I would write by hand manually. (Of course, my goal is not to have to unplug the code generator, but if something strange happens, such as a fatal flaw in the generator, then the fall-back must be to manage the code by hand.)

Handling built-in system-level code-table values was bothering me. The common example is “Roles”. I needed a generated output class that could help. I wanted no hardcode for such values. See below for more on this topic.

Design —

I decided to build some simple T4 templates to do the work. The T4ToolBox was a nice way to get up and running quickly. Futhermore, there were some nice tutorials to follow, from Oleg Sych. In my design, there would be one, project-specific “settings template”, that would define the varying parameters for a given project, and this settings-template could not be reused across more than one project. The core templates, these must be able to be reused on any number of projects. 

I decided to use the “one Manager per database table or view” approach. Nice and simple. I would handle full-object-load and cascade-delete and etc on an as-needed basis. The generated Manager would have core CRUD operations. The hand-extended Manager partial class would have custom logic.

I decided to use the “one Entity per database table or view” approach. Nice and simple. These would just hold data. For the most part, the generated Linq To Sql entities, that are made by the VS.NET Designer, were fine. I extended using partial classes for some of the extensibility methods, such as for validation logic in OnValidate() and the like.

I decided to use interfaces to make my objects uniform across projects. These interfaces must be able to be reused on any number of projects.

I decided to generate partial classes to avoid an inheritance chain.

I decided to not hand-edit generated code ever.

I decided code generation MUST be idempotent always.

I decided to use a single-column, uniqueidentifier (Guid), business-layer-generated, primary key in every table. It makes sense. It is easy. It is simple. It is strong. Etc. I can use a unique key for other lookup types, multi-column association tables, natural keys, etc.

Solution —

As above, I used T4, T4 ToolBox, Linq To Sql Entity Base, Repository design pattern, and a few other goodies.

For the CodeTable template, note that the output class may be used in many ways. For example, to make a call to the built-in method this.User.IsInRole(string targetRoleName), such as to enforce the business rule that says “hide this button if the user is not in the admin role”, then one must have somewhere hardcoded that targetRoleName. Generally, one either hard-codes the value somewhere or do this one does other odd tricks, each of which is manually maintained. Objection 1: If the code table value gets deleted or changed then you have to recompile. Answer 1: Well, what better way is there to solve the issue of calling IsUserInRole() with a string role name? This is not for everyone or everything. The hand-hardcode would break too and this is easier to maintain. Also, this risk can be mitigated by a controlled admin workflow and other code. Also, this is intended for semi-static, system-level values.

Interface —

This is just a taste of the code and a version as of about 2009-Dec-14 and a more-recent version probably exists at the Download Link. This is the interface. It may change. It seems is more-or-less done right now, I think. Please note that if this interface does change, then I might not update this in-line code sample in this post. However, the code download link above will have the latest version. The following is just to give the reader an idea of the interface.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;

namespace Team.Framework.Interfaces.BusinessLayer.BusinessEntities
{
	/// <summary>
	/// This is a shared manager interface for projects using Linq To Sql (AKA L2s).
	/// </summary>
	/// <remarks>
	/// Note that implementation is at discretion of the architect; but, recommendations are noted below.
	/// </remarks>
	public interface IL2sEntityManager
	{
		/// <summary>
		/// This saves a new entity.
		/// </summary>
		/// <param name="targetEntity">This is the entity to be saved.</param>
		/// <remarks>
		/// Note that the implementation must cast to the appropriate type.
		/// </remarks>
		void Create(object targetEntity);

		/// <summary>
		/// This deletes an existing entity if the ID does exist.
		/// </summary>
		/// <param name="targetPkId">This</param>
		/// <remarks>
		/// Note that it is recommended that this method should gracefully do nothing if the ID does not exist.
		/// Note that the ID is of type "object" in order to support all key types, such as Guid and Long.
		/// </remarks>
		void DeleteByPkId(object targetPkId);

		/// <summary>
		/// This deletes an existing entity if the ID does exist, for each in the given list.
		/// </summary>
		/// <param name="targetPkIdList">This is the list of ID values to delete.</param>
		/// <remarks>
		/// Note that it is recommended that this method should gracefully do nothing if the ID does not exist.
		/// Note that the ID is of type "ArrayList" in order to support all key types, such as Guid and Long.
		/// </remarks>
		void DeleteByPkId(ArrayList targetPkIdList);

		/// <summary>
		/// This returns the total count.
		/// </summary>
		/// <returns>This is the count or zero if there are no rows.</returns>
		long GetCount();

		/// <summary>
		/// This will instantiate and return a new, non-null, unsaved, empty object using the object's default constructor.
		/// </summary>
		/// <returns>This is the new object.</returns>
		/// <remarks>
		/// Note that the callsite must cast to the appropriate type.
		/// </remarks>
		object Instantiate();

		/// <summary>
		/// This returns a flag that indicates object existence.
		/// </summary>
		/// <param name="targetPkId">This is the ID to check.</param>
		/// <returns>This is "true" if the ID does exist, otherwise this is "false".</returns>
		bool IsExistingPkId(object targetPkId);

		/// <summary>
		/// This returns all entities or it returns null if none are found.
		/// </summary>
		/// <returns>This is the set of entities.</returns>
		/// <remarks>
		/// Note that the callsite must cast to the appropriate type.
		/// </remarks>
		System.Linq.IQueryable RetrieveAll();

		/// <summary>
		/// This returns an entity if ID does exists otherwise it returns null.
		/// </summary>
		/// <param name="targetPkId">This is the ID to check.</param>
		/// <returns>This is the entity if the ID does exists or null if the ID does not exist.</returns>
		/// <remarks>
		/// Note that the callsite must cast to the appropriate type.
		/// </remarks>
		object RetrieveByPkId(object targetPkId);

		/// <summary>
		/// This saves an existing entity.
		/// </summary>
		/// <param name="targetEntity">This is the entity to save.</param>
		void Update(object targetEntity);
	}
}

Closing —

 
I am going to stop writing there for now. I have no more time at the moment. I admit, it is all far from perfect, but it is a start.

Comments, criticism, etc, are welcome– just no swearing please.

🙂

HTH.

Thank you.

— Mark Kamoski

T4, T4 ToolBox, Linq To Sql Entity Base, Code Sample

Overview —

The purpose of this page is to provide links to the latest-and-greatest version of my T4 code generation sample.

Code Description —

The detailed description of the code can be found at the following link.

https://mkamoski1.wordpress.com/2009/12/01/t4-t4-toolbox-linq-to-sql-entity-base-code-description/

Download —

The code can be downloaded from the following link(s).

Always get the one with the latest date-time stamp at the end.

http://www.LogicBus.net/Downloads/Northwind01_T4Sample_200911091337.zip

http://www.LogicBus.net/Downloads/Northwind01_T4Sample_200912011643.zip

HTH.

Thank you.

— Mark Kamoski