RWE: Sample Code Review
        
      
      I have got a chance to do code review for one of our client's recently and I thought I would share results with you. You should learn a bit more about our API and how it influences overall website performance. I would also like to use this sample code review to demonstrate what we can do as part of Kentico Professional Services.
      
	What was an issue?
	The customer reached us because their custom code (below) was performing very poorly. The client actually reported that single document insert action takes anywhere from 6 to 10 seconds. You would probably agree there must be something wrong with the code as 10 seconds for simple insert is just too much. And you are right - it is very suspicious whenever single action takes so long.
	
	The custom code I attached below imports a new content into Kentico CMS using our API. Source data are retrieved through the channel to external webservice. Perhaps the performance loss can be caused by the delay in the response coming from the webservice. That's true; you cannot really rely on external service. There is no guarantee service would be available all the time and there will always be the factor of hand-shake and request-response cost. Anyway, for this particular case let's assume the delay is no longer than 1 to 3 seconds. We are therefore still looking at additional 7-9 seconds of the execution time lost somewhere along the lines of our custom code.
	
	Let's take closer look on the code now.
	How does code look like?
	In my analysis I am going to refer to the custom code below. Please review it before you move forward. Please note that for obvious reasons I replaced name of variables and certain lines of code that could suggest who the customer is.
	
		Code Snippet
	
		
			- 
				namespace CUSTOM.Business
- 
				{
- 
				    /// <summary>
- 
				    /// 
- 
				    /// </summary>
- 
				    public static class LocationManager
- 
				    {
- 
				        private static LocationManagerDataSet _lmds;
- 
				        private static BranchManagerDataSet _bmds;
- 
				        private static AgeRangeDataSet _agds;
- 
				        private static WebTimeCrossReferenceManagerDataSet _wtcds;
- 
				        private static WebTimeSFYPManagerDataSet _wtsds;
- 
				        private static WebTimeItemCrossReferenceManagerDataSet _wtixds;
- 
				        private static JobScheduleDataSet _jsds;
- 
				 
- 
				        private static DataSet ClassesDataSet;
- 
				        private static string durationstr;
- 
				        private static string currentSite;
- 
				        private static string currentCulture;        
- 
				 
- 
				        public static string CurrentSite
- 
				        {
- 
				            get
- 
				            {
- 
				                if (string.IsNullOrEmpty(currentSite))
- 
				                {
- 
				                    if (CMS.CMSHelper.CMSContext.CurrentSiteName == string.Empty)
- 
				                        currentSite = ConfigurationManager.AppSettings["DefaultSite"].ToString();
- 
				                    else
- 
				                        currentSite = CMS.CMSHelper.CMSContext.CurrentSiteName;
- 
				                }
- 
				                return currentSite;
- 
				            }
- 
				        }
- 
				 
- 
				        public static string CurrentCulture
- 
				        {
- 
				            get
- 
				            {
- 
				                if (string.IsNullOrEmpty(currentCulture))
- 
				                {
- 
				                    if (CMS.CMSHelper.CMSContext.CurrentSiteName == string.Empty)
- 
				                        currentCulture = ConfigurationManager.AppSettings["DefaultCulture"].ToString();
- 
				                    else
- 
				                        currentCulture = CMS.CMSHelper.CMSContext.CurrentDocumentCulture.CultureCode;
- 
				                }
- 
				                return currentCulture;
- 
				            }
- 
				        }
- 
				 
- 
				        public static LocationManagerDataSet GetAllLocations()
- 
				        {
- 
				            _lmds = SqlDataClient.GetAllLocations();
- 
				            return _lmds;
- 
				        }
- 
				 
- 
				        public static JobScheduleDataSet GetAllJobBranches()
- 
				        {
- 
				            _jsds = SqlDataClient.GetAllJobBranches();
- 
				            return _jsds;
- 
				        }
- 
				 
- 
				        public static void UpdateJobBranch(int locationID)
- 
				        {
- 
				            SqlDataClient.UpdateJobBranch(locationID);
- 
				        }
- 
				 
- 
				 
- 
				        public static AgeRangeDataSet GetAllAgeRange()
- 
				        {
- 
				            _agds = SqlDataClient.GetAllAgeRange();
- 
				            return _agds;
- 
				        }
- 
				        
- 
				        public static LocationManagerDataSet GetLocation(int LocationID)
- 
				        {
- 
				            _lmds = SqlDataClient.GetLocation(LocationID);
- 
				            return _lmds;
- 
				        }
- 
				        
- 
				        public static BranchManagerDataSet GetBranchByBranchName(string branchName)
- 
				        {
- 
				            _bmds = SqlDataClient.GetBranchByBranchName(branchName);
- 
				            return _bmds;
- 
				        }
- 
				        
- 
				        public static LocationManagerDataSet GetLocationDataSet(int LocationID)
- 
				        {
- 
				            _lmds = SqlDataClient.GetLocation(LocationID);
- 
				            return _lmds;
- 
				        }
- 
				 
- 
				        public static BranchManagerDataSet GetValidWebTimeBranches()
- 
				        {
- 
				            _bmds = SqlDataClient.GetValidBranches();
- 
				            return _bmds;
- 
				        }
- 
				 
- 
				        public static void GetWebItemItemCrossReferenceByLocationID(int locationID)
- 
				        {
- 
				            _wtixds = SqlDataClient.GetWebItemItemCrossReferenceByLocationID(locationID);
- 
				        }
- 
				 
- 
				        public static void GetWebTimeCrossReferenceByLocationID(int locationID)
- 
				        {
- 
				            _wtcds = SqlDataClient.GetWebTimeCrossReferenceByLocationID(locationID);
- 
				        }
- 
				 
- 
				        public static void GetWebTimeSFYPDataByLocationID(int locationID)
- 
				        {
- 
				            _wtsds = SqlDataClient.GetWebTimeSFYPDataByLocationID(locationID);
- 
				        }
- 
				 
- 
				        public static string RefreshDataFromWebService(int locationID)
- 
				        {
- 
				            DateTime startTime = DateTime.Now;
- 
				            GetLocation(locationID);
- 
				 
- 
				            Guid batchNumber = Guid.NewGuid();
- 
				 
- 
				            foreach (LocationManagerDataSet.LocationDataTableRow drow in _lmds.LocationDataTable.Rows)
- 
				            {
- 
				                // Create the web request   
- 
				                HttpWebRequest request = WebRequest.Create(string.Format("http://www.somedomain.com", drow.WebserviceLocationID)) as HttpWebRequest;
- 
				                
- 
				                if (request != null)
- 
				                {
- 
				                    // Get response   
- 
				                    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
- 
				                    {
- 
				                        if (response != null)
- 
				                        {
- 
				                            string contents = string.Empty;
- 
				                            // Load data into a dataset   
- 
				                            DataSet dsw = new DataSet();
- 
				                            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
- 
				                            {
- 
				                                contents = reader.ReadToEnd().Replace("& UP", "+");
- 
				                            }
- 
				 
- 
				                            System.IO.StringReader strcontents = new System.IO.StringReader(contents);
- 
				                            WebTimeDataManagerDataSet dsWeather = new WebTimeDataManagerDataSet();
- 
				                            dsw.ReadXml(strcontents);
- 
				 
- 
				                            if (dsw.Tables["Transaction"].Rows[0]["TRAN_STATUS"].ToString() != "FAIL")
- 
				                            {
- 
				                                // save the data
- 
				                                SqlDataClient.SaveWebTimeData(dsw, drow.WebtimeLocationID, batchNumber);
- 
				                            }
- 
				                        }
- 
				                    }
- 
				                }
- 
				            }
- 
				 
- 
				            DateTime stopTime = DateTime.Now;
- 
				            TimeSpan duration = stopTime - startTime;
- 
				            durationstr = duration.ToString();
- 
				            return ("RefreshTimes: " + durationstr);
- 
				        }
- 
				 
- 
				        public static string PopulateNodes(int locationID)
- 
				        {
- 
				            durationstr = string.Empty;
- 
				 
- 
				            GetLocation(locationID);
- 
				            GetValidWebTimeBranches();
- 
				 
- 
				            foreach (LocationManagerDataSet.LocationDataTableRow drow in _lmds.LocationDataTable.Rows)
- 
				            {
- 
				                DeleteSchedules(drow.WebtimeLocationID, _bmds);
- 
				                AddNewSchedules(drow.WebtimeLocationID, _bmds);
- 
				            }
- 
				 
- 
				            return (durationstr);
- 
				        }
- 
				 
- 
				        private static void DeleteSchedules(int locationID, BranchManagerDataSet bm)
- 
				        {
- 
				            DateTime startTime = DateTime.Now;
- 
				            try
- 
				            {
- 
				                // look for the Programs Nodes
- 
				                DataRow[] drow = bm.something_Branch.Select("WebtimeLocationID=" + locationID.ToString());
- 
				                string programpath = string.Empty;
- 
				                string unprocessedpath = string.Empty;
- 
				 
- 
				                if (drow.Count() > 0)
- 
				                {
- 
				                    programpath = ((BranchManagerDataSet.something_BranchRow)drow[0]).NodeAliasPath.ToString() + "/Programs/%";
- 
				                }
- 
				 
- 
				                // if there is a real program path, go to the path using the Kentico API 
- 
				                if (programpath != string.Empty)
- 
				                {
- 
				                    // create a TreeProvider instance
- 
				                    UserInfo ui = UserInfoProvider.GetUserInfo("administrator");
- 
				                    CMS.TreeEngine.TreeProvider tree = new CMS.TreeEngine.TreeProvider(ui);
- 
				 
- 
				                    DataSet treeDS = tree.SelectNodes(CurrentSite, programpath, CurrentCulture, true, "something.ClassTime");
- 
				                    if (treeDS != null && treeDS.Tables.Count > 0)
- 
				                    {
- 
				                        CMS.CMSHelper.CMSContext.Init();
- 
				                        DataTable dtable = treeDS.Tables[0];
- 
				                        foreach (DataRow dr in dtable.Rows)
- 
				                        {
- 
				                            // Get document with specified site, aliaspath and culture
- 
				 
- 
				                            CMS.TreeEngine.TreeNode node = tree.SelectSingleNode(CurrentSite, dr["NodeAliaspath"].ToString(), CurrentCulture, true, "something.ClassTime");
- 
				                            if (node != null)
- 
				                            {
- 
				                                // Delete the document
- 
				                                DocumentHelper.DeleteDocument(node, tree, true, true, true);
- 
				                                node.Delete();
- 
				                            }
- 
				                        }
- 
				                    }
- 
				 
- 
				                    // delete also from the Unprocessed Bucket
- 
				                    unprocessedpath = "/Unprocessed" + ((BranchManagerDataSet.something_BranchRow)drow[0]).NodeAliasPath + "/%";
- 
				                    DataSet unprocessedDS = new DataSet();
- 
				 
- 
				                    unprocessedDS = tree.SelectNodes(CurrentSite, unprocessedpath, CurrentCulture, true, "something.ClassTime");
- 
				                    if (unprocessedDS != null && unprocessedDS.Tables.Count > 0)
- 
				                    {
- 
				                        // this is needed for the windows service routine because it is not in context of the website
- 
				                        CMS.CMSHelper.CMSContext.Init();
- 
				                        DataTable unprocesseddtable = unprocessedDS.Tables[0];
- 
				                        foreach (DataRow dr in unprocesseddtable.Rows)
- 
				                        {
- 
				                            // Get document with specified site, aliaspath and culture
- 
				 
- 
				                            CMS.TreeEngine.TreeNode node = tree.SelectSingleNode(CurrentSite, dr["NodeAliaspath"].ToString(), CurrentCulture, true, "something.ClassTime");
- 
				                            if (node != null)
- 
				                            {
- 
				                                // Delete the document
- 
				                                DocumentHelper.DeleteDocument(node, tree, true, true, true);
- 
				                                node.Delete();
- 
				                            }
- 
				                        }
- 
				                    }
- 
				                }
- 
				 
- 
				                DateTime stopTime = DateTime.Now;
- 
				                TimeSpan duration = stopTime - startTime;
- 
				                durationstr = "Delete Times: " + duration.ToString();
- 
				            }
- 
				            catch (Exception ex)
- 
				            {
- 
				                throw new Exception(ex.Message);
- 
				            }
- 
				        }        
- 
				 
- 
				        public static CMSTreeNodeOfferingCategoryManagerDataSet GetOfferingCategoryNodesByBranchPath(string branchnamepath)
- 
				        {
- 
				            CMSTreeNodeOfferingCategoryManagerDataSet currentProgramsDS = new CMSTreeNodeOfferingCategoryManagerDataSet();
- 
				 
- 
				            UserInfo ui = UserInfoProvider.GetUserInfo("administrator");
- 
				            CMS.TreeEngine.TreeProvider tree = new CMS.TreeEngine.TreeProvider(ui);
- 
				 
- 
				            currentProgramsDS.CMSTreeNodeOfferingCategory.TableName = "something.OfferingCategoryPage";
- 
				 
- 
				            string programpath = branchnamepath + "/Programs/%";
- 
				 
- 
				            // get all the program offerings for this program path
- 
				            currentProgramsDS.Merge(tree.SelectNodes(CurrentSite, programpath, CurrentCulture, true, "something.OfferingCategoryPage"));
- 
				 
- 
				            return currentProgramsDS;
- 
				        }
- 
				 
- 
				        public static CMSTreeNodeOfferingCategoryManagerDataSet GetOfferingCategoryNodes(string branchpath)
- 
				        {
- 
				            CMSTreeNodeOfferingCategoryManagerDataSet currentProgramsDS = new CMSTreeNodeOfferingCategoryManagerDataSet();
- 
				 
- 
				            UserInfo ui = UserInfoProvider.GetUserInfo("administrator");
- 
				            CMS.TreeEngine.TreeProvider tree = new CMS.TreeEngine.TreeProvider(ui);
- 
				 
- 
				            currentProgramsDS.CMSTreeNodeOfferingCategory.TableName = "something.OfferingCategoryPage";
- 
				 
- 
				            string programpath = branchpath + "/Programs/%";
- 
				 
- 
				            // get all the program offerings for this program path
- 
				            currentProgramsDS.Merge(tree.SelectNodes(CurrentSite, programpath, CurrentCulture, true, "something.OfferingCategoryPage"));
- 
				 
- 
				            return currentProgramsDS;
- 
				        }        
- 
				 
- 
				        private static void AddNewSchedules(int locationID, BranchManagerDataSet bm)
- 
				        {
- 
				            // look for the Programs Nodes
- 
				            DataRow[] drow = bm.something_Branch.Select("WebtimeLocationID=" + locationID.ToString());
- 
				            string programpath = string.Empty;
- 
				 
- 
				            DateTime startTime = DateTime.Now;
- 
				 
- 
				            try
- 
				            {
- 
				                if (drow.Count() > 0)
- 
				                {
- 
				                    programpath = ((BranchManagerDataSet.something_BranchRow)drow[0]).NodeAliasPath + "/Programs/%";
- 
				 
- 
				                    // Get the Import Data
- 
				                    GetWebTimeSFYPDataByLocationID(locationID);
- 
				                    GetWebItemItemCrossReferenceByLocationID(locationID);
- 
				 
- 
				                    UserInfo ui = UserInfoProvider.GetUserInfo("administrator");
- 
				                    CMS.TreeEngine.TreeProvider tree = new CMS.TreeEngine.TreeProvider(ui);
- 
				 
- 
				                    CMSTreeNodeClassTypeDataSet ClassesDS = new CMSTreeNodeClassTypeDataSet();
- 
				                    ClassesDS.CMSTreeNodeClassType.TableName = "something.Class";
- 
				 
- 
				                    // get all the classes for this program path
- 
				 
- 
				                    DataSet ds = tree.SelectNodes(CurrentSite, programpath, CurrentCulture, true, "something.Class");
- 
				                    ClassesDS.Merge(ds);
- 
				 
- 
				                    //Read the Import Data First
- 
				                    foreach (WebTimeSFYPManagerDataSet.SOMETHING_WebTimeSFYPDataTableRow dsrow in _wtsds.SOMETHING_WebTimeSFYPDataTable.Rows)
- 
				                    {                        
- 
				                        // locate for the pgm code in the crossreference file
- 
				                        string pgmcodetosearch = string.Empty;
- 
				 
- 
				                        // check if the last 2 are numbers
- 
				                        if (dsrow.THRUSTCODE != string.Empty)
- 
				                        {
- 
				                            // create exception
- 
				                            // look for the Programs Nodes
- 
				                            string selectstring = "WebTime_ThrustCode='" + dsrow.THRUSTCODE.ToString() + "'";
- 
				                            DataRow[] xrows = ClassesDS.Tables[0].Select(selectstring);
- 
				 
- 
				                            if (xrows.Count() > 0)
- 
				                            {
- 
				                                // get parent node for new document
- 
				                                CreateNode(((CMSTreeNodeClassTypeDataSet.CMSTreeNodeClassTypeRow)xrows[0]).NodeAliasPath, tree, dsrow, ((BranchManagerDataSet.something_BranchRow)drow[0]).WebserviceLocationID.ToString());
- 
				                            }
- 
				                            else
- 
				                            {
- 
				                                // create exception
- 
				                                // look for the Programs Nodes
- 
				                                string parentpath = string.Empty;
- 
				 
- 
				                                if (drow.Count() > 0)
- 
				                                {
- 
				                                    parentpath = "/Unprocessed" + ((BranchManagerDataSet.something_BranchRow)drow[0]).NodeAliasPath;
- 
				                                }
- 
				 
- 
				                                CreateNode(parentpath, tree, dsrow, ((BranchManagerDataSet.something_BranchRow)drow[0]).WebserviceLocationID.ToString());
- 
				                            }
- 
				                        }
- 
				                        else
- 
				                        {
- 
				                            // create exception
- 
				                            // look for the Programs Nodes
- 
				                            string parentpath = string.Empty;
- 
				 
- 
				                            if (drow.Count() > 0)
- 
				                            {
- 
				                                parentpath = "/Unprocessed" + ((BranchManagerDataSet.something_BranchRow)drow[0]).NodeAliasPath;
- 
				                            }
- 
				 
- 
				                            CreateNode(parentpath, tree, dsrow, ((BranchManagerDataSet.something_BranchRow)drow[0]).WebserviceLocationID.ToString());
- 
				                        }
- 
				                    }
- 
				                }
- 
				 
- 
				                DateTime stopTime = DateTime.Now;
- 
				                TimeSpan duration = stopTime - startTime;
- 
				 
- 
				                durationstr = durationstr + "<br/>" + "Add Times: " + duration.ToString() + "<br>";
- 
				            }
- 
				            catch (Exception ex)
- 
				            {
- 
				                throw new Exception(ex.Message);
- 
				            }
- 
				        }
- 
				 
- 
				        private static void CreateNode(string parentpath, CMS.TreeEngine.TreeProvider tree, WebTimeSFYPManagerDataSet.SOMETHING_WebTimeSFYPDataTableRow dsrow, string webservicelocationID)
- 
				        {
- 
				            // get parent node for new document
- 
				 
- 
				            // this is needed for the windows service because it is not in the context of the website    
- 
				            CMS.CMSHelper.CMSContext.Init();
- 
				            CMS.TreeEngine.TreeNode parentClassNode = tree.SelectSingleNode(CurrentSite, parentpath, CurrentCulture);
- 
				 
- 
				            // create a new tree node
- 
				            CMS.TreeEngine.TreeNode node = new CMS.TreeEngine.TreeNode("something.ClassTime", tree);
- 
				 
- 
				            // check off the properties
- 
				            if (parentClassNode != null)
- 
				            {
- 
				                // set document properties
- 
				                string nodename = GenerateNodeName(dsrow);
- 
				                if (nodename == string.Empty)
- 
				                    nodename = dsrow.KEY;
- 
				 
- 
				                node.NodeName = nodename;
- 
				                node.NodeAlias = nodename;
- 
				 
- 
				                node.SetValue("DocumentCulture", CurrentCulture);
- 
				                node.SetValue("DayOfWeek", nodename);
- 
				                node.SetValue("DocumentName", nodename);
- 
				 
- 
				                string startdate = string.Empty;
- 
				                string enddate = string.Empty;
- 
				 
- 
				                if (dsrow.BEGIN_DATE != string.Empty)
- 
				                {
- 
				                    startdate = DateTime.Now.Year.ToString().Substring(0, 2) + dsrow.BEGIN_DATE;
- 
				                    node.SetValue("StartDate", Convert.ToDateTime(startdate.Substring(0, 4) + "/" + startdate.Substring(4, 2) + "/" + startdate.Substring(6, 2)));
- 
				                }
- 
				 
- 
				                if (dsrow.END_DATE != string.Empty)
- 
				                {
- 
				                    enddate = DateTime.Now.Year.ToString().Substring(0, 2) + dsrow.END_DATE;
- 
				                    node.SetValue("EndDate", Convert.ToDateTime(enddate.Substring(0, 4) + "/" + enddate.Substring(4, 2) + "/" + enddate.Substring(6, 2)));
- 
				                }
- 
				 
- 
				                node.SetValue("StartTime", dsrow.START_TIME);
- 
				                node.SetValue("EndTime", dsrow.END_TIME); 
- 
				                node.Insert(parentClassNode.NodeID);
- 
				 
- 
				                WorkflowManager wm = new WorkflowManager(tree);
- 
				 
- 
				                //check if the node supports workflow
- 
				                WorkflowInfo wi = wm.GetNodeWorkflow(node);
- 
				                if (wi != null)
- 
				                {
- 
				                    // Approve until the step is publish
- 
				                    WorkflowStepInfo currentStep = wm.GetStepInfo(node);
- 
				                    if (wm != null)
- 
				                    {
- 
				                        while ((currentStep != null) && (currentStep.StepName.ToLower() != "published"))
- 
				                        {
- 
				                            currentStep = wm.MoveToNextStep(node, "");
- 
				                        }
- 
				                    }
- 
				                }
- 
				            }
- 
				        }
- 
				    }
- 
				}
 
 
	
	How to improve existing code?
	
	Problem 1: Performing Unnecessary Actions
	Let's focus on the PopulateNodes(int locationID) method first. As you can see for every location (basically row record) in the _lmds.LocationDataTable.Rows collection the custom code calls DeleteSchedules(int locationID, BranchManagerDataSet bm) method (line no. 187). Inside this method our code retrieves the collection of 'Programs' nodes existing in the content tree by using the TreeProvide.SelectNodes() method (line no. 216).
	
	As the next step code loops through the retrieved program nodes and for every node it:
	a) Gets the 
NodeAliasPath available in the node data row (line no. 
225),
	
	b) Query the database to retrieve the 
TreeNode object using this 
NodeAliasPath for the 
TreeProvider.SelectSingleNode() method (line no. 
225),
	 
FIX 1: There is actually no reason to call the 
SelectSingleNode() method to get 
TreeNode object. All the information about the node is already present in the 
DataRow dr (line no. 
221). Thereby, instead of calling the 
SelectSingleNode() we can create new instance of the 
TreeNode object using the 
new TreeNode(dr, "<document type name>", treeProvider) constructor where we simply pass the 
DataRow as parameter.
	 
The small change suggested above saves 1 query per every program node per every location. If there would be 100 locations with let's say only 10 program nodes per location we would have just saved 1000 quite expensive queries.
	Problem 2: Duplicate Actions 
There is another issue with the 
DeleteSchedules(int locationID, BranchManagerDataSet bm) method. You can see code calls the 
DocumentHelper.DeleteDocument() method first and then 
the node.Delete() method right after that (lines no. 
229 and 
230). The 
DocumentHelper.DeleteDocument() should be used for documents under the workflow. Hence it is absolutely correct to call 
DeleteDocument() as long as we are dealing with versioned documents. The 
node.Delete() method on the other hand should be used only for documents without workflow because unlike 
DocumentHelper.DeleteDocument() the 
node.Delete() does not care about multiple versions of the document and therefore is not suitable in our scenario.
FIX 2: There is no reason to call both the 
DocumentHelper.DeleteDocument() and 
node.Delete() method at the same place. Actually, it is highly inefficient because 
the node.Delete() executes additional query for every program node for every location after the document is already wiped off by 
DeleteDocument() called earlier.
We can again save hundreds or even thousands of queries just by removing the 
node.Delete() call.
NOTE: The same fix needs to be applied to the code few lines later when working through bucket of unprocessed documents (lines no. 
235 - 258).
	Problem 3: No Data Caching & Inefficient Insert Operation
Inside the 
AddNewSchedules(int locationID, BranchManagerDataSet bm) method custom code calls the 
GetWebTimeSFYPDataByLocationID(int locationID) and 
GetWebItemItemCrossReferenceByLocationID(int locationID) methods (lines no. 
319 and 
320) that retrieve data from database executing 2 queries in total for every location. Further in the same method code also retrieves all the location's program nodes (line no. 
330). The code then loops through the import data (source data for new document sent from external service) and calls the 
CreateNode(string parentpath, CMS.TreeEngine.TreeProvider tree, WebTimeSFYPManagerDataSet.SOMETHING_WebTimeSFYPDataTableRow dsrow, string webservicelocationID) method to insert new document in the content tree (lines no. 
350, 
363 and 
377).
In the 
CreateNode() method custom code uses 
TreeProvider.SelectSingleNode() (line no. 
399) to get the parent node required whenever we are inserting a new document. The 
CreateNode() method accepts parent 
NodeAliasPath as first parameter. This parent 
NodeAliasPath comes from the 
DataRow that contains full information on the parent node - like 
NodeID for example.
At the bottom of the 
CreateNode() method code uses the 
node.Insert(parentNodeID) (line no. 
436) to finally insert a new document in the content tree.
FIX 3: As you can see most of the custom methods (lines no. 
68 - 129) pulling out additional information related to the document created (e.g. location details) execute single query for every location being processed. The results of those queries are not cached at all even though caching is perfect fit in scenarios like this. We should therefore implement caching mechanism for the location data obtained from database to streamline data retrieval and processing. The new API used to simplify caching implementations is explained 
here.
Ideally we should go even step further and instead of querying DB for every location separately we could retrieve details for all locations within the single request, cache the whole result set and later filter out records from the 
DataSet stored in a memory rather than accessing DB over and over again.
FIX 4: : Instead of selecting the parent node object using the 
TreeProvider.SelectSingleNode() (line no. 
399) using the parent 
NodeAliasPath we want to pass the parent 
NodeID as input parameter for the 
CreateNode() method. The parent 
NodeID is available in the same 
DataRow as 
NodeAliasPath in the original code. This would save us another call to the database curently fired by 
TreeProvider.SelectSingleNode() (line no. 
399). If you realize that every call to the 
SelectSingleNode() executes additional query to get parent information already available we can eliminate another query for every document created.
FIX 5: To further optimize the code inserting new documents we want to call the 
node.Insert(parentNodeObj) instead of 
node.Insert(parentNodeID) (line no. 
436). If you call the 
node.Insert() method with the parent 
NodeID as parameter the 
Insert() method will still internally call the 
SelectSingleNode() to get the parent node object. Let's call the 
Insert() with the parent node object directly and we get rid of another expensive query. As I have already mentioned to create new instance of parent 
TreeNode object we just need to use the 
new TreeNode() constructor and pass the parent 
DataRow as one of its parameters.
	 
	FIX 6: In the  CreateNode() method we should use the DocumentHelper.InsertDocument() instead of the node.Insert(). The DocumentHelper.InsertDocument() recognizes whether the node uses workflow or not and performs not only insert of the document itself but also takes care about any additional actions required to properly handle versioning and other workflow related functionality.
	Summary
As you could see when it comes to the performance issues with your custom code is more-or-less about efficiency of your code. And by most part the efficiency goes hand in hand with costs of data access and transfer. That being said your attention should be aimed on how much data you retrieve from data base, what is reusability level of such data and whether or not the same data are not already available at different source.
	Before you go...
I would love to hear some feedback from you on whether you find posts like this useful, what I should change or add to better serve their purpose and so on. I can imagine we could do some series from articles on code review/best practices topics, however, I need to know if it is something you may be interested in. Please use comments below to pass the message.
Thank you!
	K.J.