Kentico CMS Mapper functions using Reflection in C# .NET

Posted on October 17, 2013 by Michael Roma

The following post shows functionality on mapping a Kentico CMS TreeNode object to a strong type .NET model class. These functions use reflection to map a property in a class to a Kentico document type field.

The examples below assumes your Document Types in Kentico have a corresponding .NET model class.

Below is the base class BaseModel that all model classes inherit from:

using CMS.PortalControls;
using CMS.DocumentEngine;
using CMS.CMSHelper;
using CMS.SettingsProvider;

public abstract class BaseModel
{
	// define document id
	public int id
	{
		get { return Document == null ? 0 : Document.DocumentID; }
	}

	// define node id
	public int NodeId
	{
		get { return Document != null ? Document.NodeID : 0; }
	}

	// define name
	public string DocumentName
	{
		get { return Document != null ? Document.DocumentName : ""; }
	}

	// define url
	public string URL
	{
		get { return Document == null ? "" : Document.RelativeURL.Replace("~",""); }
	}

	// define url with extension
	public string URLWithExtension
	{
		get { return URL + (Document == null ? "" : Document.DocumentExtensions); }
	}

	// external url
	public string ExternalURL
	{
		get { return Document == null ? "" : Document.AbsoluteURL; }
	}        

	// path for searching
	public string Path
	{
		get { return Document == null ? "" : URL + (!URL.EndsWith("/") ? "/" : "") + "%"; }
	}

	// define document
	public TreeNode Document { get; set; }


	// define function that maps extra properties
	public abstract void MapExtras();
}

The following are helper types to define an Image and a Generic Document:

public class Image
{
	// fields
	public string Guid { get; set; }

	// function that checks if an image
	public bool HasImage { get { return !String.IsNullOrEmpty(Guid); } } 

	// define path for the image
	public string Path
	{
		get { return String.Format("/getfile/{0}/img/", Guid); }
	}

	// function that returns the html tag for this image
	public string ToHtml(string alt)
	{
		// check if no guid
		if (String.IsNullOrEmpty(Guid))
			return "";

		// return the html tag for this image
		return String.Format("\"{1}\"", Path, alt);            
	}
}   
public class GenericDocument : BaseModel
{
	public override void MapExtras() { }
}

Below is a generic mapper function to map a given TreeNode and return the requested type:

public static T MapTo(TreeNode document)
	where T : BaseModel, new()
{
	// init item
	T item = new T();

	// check if a document passed
	if (document != null)
	{
		// set the document
		item.Document = document;

		// go through each of this model's properties
		foreach (var p in item.GetType().GetProperties())
		{
			// check if a writeable property
			if (p.CanWrite)
			{
				// check primitive types
				if (p.PropertyType == typeof(string))
				{
					p.SetValue(item, document.GetStringValue(p.Name, ""), null);
				}
				else if (p.PropertyType == typeof(int))
				{
					p.SetValue(item, document.GetIntegerValue(p.Name, 0), null);
				}
				else if (p.PropertyType == typeof(bool))
				{
					p.SetValue(item, document.GetBooleanValue(p.Name, false), null);
				}
				else if (p.PropertyType == typeof(double))
				{
					p.SetValue(item, document.GetDoubleValue(p.Name, 0), null);
				}
				else if (p.PropertyType == typeof(Guid))
				{
					p.SetValue(item, document.GetGuidValue(p.Name, new Guid()), null);
				}
				else if (p.PropertyType == typeof(DateTime))
				{
					p.SetValue(item, document.GetDateTimeValue(p.Name, DateTime.Parse("1/1/1900").Date), null);
				}

				// check complex types
				else if (p.PropertyType == typeof(Image))
				{
					p.SetValue(item, new Image { Guid = document.GetStringValue(p.Name, "") }, null);
				}				
				else if (p.PropertyType == typeof(GenericDocument))
				{
					p.SetValue(item, MapTo(GetDocumentFromGUID(document.GetStringValue(p.Name, ""))), null);
				}			
			}
		}

		// map extras
		item.MapExtras();
	}

	// return the item
	return item;
}

Below is a generic mapper function to map a given TreeNodeDataSet and return a list of the requested type:

public static List MapToList(TreeNodeDataSet documentList)
	where T : BaseModel, new()
{
	// define return list
	List list = new List();

	// check if a document list
	if (documentList != null)
	{
		// go through each
		foreach (var n in documentList)
		{
			list.Add(MapTo(n));
		}
	}

	// return 
	return list;
}

Below are helper functions needed for mapping:

// function that gets a tree node from guid
public static TreeNode GetDocumentFromGUID(string guidStr)
{
	// check if no guid
	Guid guid;
	if (Guid.TryParse(guidStr, out guid))
	{
		// get a tree provider
		var th = new TreeProvider();

		// find the node
		var n = th.SelectSingleNode(guid, CultureCode, CMSContext.CurrentSiteName);

		// check if a node
		if (n != null)
		{
			return DocumentHelper.GetDocument(n, th);
		}
	}

	// if here, return nothing
	return null;
}

// function that gets a tree node from id
public static TreeNode GetDocumentFromID(int id)
{
	// check if no id            
	if (id > 0)
	{
		// get a tree provider
		var th = new TreeProvider();
		return DocumentHelper.GetDocument(id, th);                
	}

	// if here, return nothing
	return null;
}

// function that trys to find the current culture, falls back to default
public static string CultureCode
{
	get
	{
		// return nothing if blank
		return CMSContext.CurrentDocumentCulture == null ? "en-US"
			: CMSContext.CurrentDocumentCulture.CultureCode;
	}
}

Below is an example on using this functionality:

// blog post model
public class BlogPost : BaseModel
{
	// fields
	public int BlogPostID { get; set; }
	public string Title { get; set; }
	public DateTime PostDate { get; set; }
	public string PostURL { get; set; }
	public Image Photo { get; set; }
	public string Author { get; set; }
	public string AuthorURL { get; set; }
	public string ReadMoreText { get; set; }

	public override void MapExtras()
	{
	}
}

// example
public class Example
{
	public static void GetSinglePost()
	{
		// get a single post from the current document
		BlogPost post = Mapper.MapTo(CurrentDocument);				
	}
	
	public static void GetPostList()
	{
		// get a list of blog posts in the path		
		var th = new TreeProvider();

		// find the nodes
		var nodes = th.SelectNodes(
			CMSContext.CurrentSiteName, "/%",
			Globals.CultureCode, false,
			"EXAMPLE.BlogPost", "", "NodeOrder", -1, true, -1);

		// get the list            
		List posts = Mapper.MapToList(nodes).OrderBy(x => x.Document.NodeOrder).ToList();
	}
}
comments powered by Disqus