/// <summary>
        /// Renders the specified <paramref name="blockProperties"/> to the output pipe.
        /// </summary>
        /// <param name="context">The <see cref="IMansionContext"/>.</param>
        /// <param name="blockProperties">The <see cref="IPropertyBag"/> of the block which to render.</param>
        /// <param name="targetField">The name of the field to which to render.</param>
        protected override void DoRender(IMansionContext context, IPropertyBag blockProperties, string targetField)
        {
            // first retrieve the block node to display
            Guid displayedBlockGuid;
            if (!blockProperties.TryGet(context, "blockGuid", out displayedBlockGuid))
                throw new InvalidOperationException("Block guid not found for shared block display");
            var displayedBlockNode = context.Repository.RetrieveSingleNode(context, new PropertyBag
                                                                                    {
                                                                                        {"guid", displayedBlockGuid}
                                                                                    });
            if (displayedBlockNode == null)
                throw new InvalidOperationException(string.Format("Could not find block with guid '{0}'", displayedBlockGuid));

            // second, merge the two block properties together
            var mergedBlockProperties = new PropertyBag();
            mergedBlockProperties.Merge(blockProperties);
            mergedBlockProperties.Merge(displayedBlockNode);
            mergedBlockProperties.Set("id", blockProperties.Get<int>(context, "id"));

            // finally re-render the combined block using the portal service
            PortalService.RenderBlock(context, mergedBlockProperties, targetField);
        }
		/// <summary>
		/// 
		/// </summary>
		/// <param name="context"></param>
		/// <param name="queryBuilder"></param>
		/// <param name="record"> </param>
		/// <param name="modifiedProperties"></param>
		protected override void DoToUpdateStatement(IMansionContext context, ModificationQueryBuilder queryBuilder, Record record, IPropertyBag modifiedProperties)
		{
			// assemble the extended properties
			var extendedProperties = new PropertyBag(record);
			extendedProperties.Merge(modifiedProperties);

			// remove all properties starting with an underscore
			var unstoredPropertyNames = extendedProperties.Names.Where(candidate => candidate.StartsWith("_")).ToList();
			foreach (var propertyName in unstoredPropertyNames)
				extendedProperties.Remove(propertyName);

			// get the conversion service
			var conversionService = context.Nucleus.ResolveSingle<IConversionService>();

			// set the column value
			queryBuilder.AddColumnValue("extendedProperties", conversionService.Convert<byte[]>(context, extendedProperties), DbType.Binary);
		}
		/// <summary>
		/// Loads the <see cref="FormState"/> of a particular <paramref name="form"/> from the <paramref name="context"/>.
		/// </summary>
		/// <param name="context">The <see cref="IMansionWebContext"/>.</param>
		/// <param name="form">The <see cref="Form"/> for which to load the state.</param>
		/// <returns>Returns the loaded <see cref="FormState"/>.</returns>
		protected override FormState DoLoadState(IMansionWebContext context, Form form)
		{
			// get the GET and POST propertybags
			var getProperties = context.Stack.Peek<IPropertyBag>("Get");
			var postProperties = context.Stack.Peek<IPropertyBag>("Post");
			var fieldProperties = new PropertyBag();
			var formProperties = new PropertyBag();
			var action = string.Empty;

			// if the form has more than one step, try to load the state
			var stateLossDetected = false;
			if (form.Steps.Count > 1)
			{
				// try to load the state
				string stateString;
				var stateKey = form.Prefix + "state";
				if (!postProperties.TryGet(context, stateKey, out stateString) || string.IsNullOrEmpty(stateString))
					stateString = getProperties.Get<string>(context, stateKey, null);

				// dehydrate the field properties, if there is state
				if (!string.IsNullOrEmpty(stateString))
				{
					// dehydrate the field properties
					var dehydratedFieldProperties = context.Nucleus.ResolveSingle<IConversionService>().Convert<IPropertyBag>(context, stateString);

					// merge them with the current properties
					fieldProperties.Merge(dehydratedFieldProperties);
				}
				else
					stateLossDetected = true;
			}

			// load the properties from the data source
			foreach (var candidate in postProperties.Where(candidate => candidate.Key.StartsWith(form.Prefix, StringComparison.OrdinalIgnoreCase)))
			{
				// check for field property
				if (candidate.Key.StartsWith(form.FieldPrefix, StringComparison.OrdinalIgnoreCase))
					fieldProperties.Add(candidate.Key.Substring(form.FieldPrefix.Length), candidate.Value);
					// check for action property
				else if (candidate.Key.StartsWith(form.ActionPrefix, StringComparison.OrdinalIgnoreCase))
					action = candidate.Value.ToString();
				else
					formProperties.Add(candidate.Key.Substring(form.Prefix.Length), candidate.Value);
			}

			// determine the current step
			var currentStepId = formProperties.Get(context, "current-step", getProperties.Get(context, form.Prefix + "current-step", 0));
			var currentStep = form.Steps.ElementAtOrDefault(currentStepId) ?? form.Steps.First();

			// always go to the first step on multi step forms when the state has been lost
			if (form.Steps.Count > 1 && stateLossDetected)
				currentStep = form.Steps.First();

			// determine the next set
			var nextStep = form.Steps.ElementAtOrDefault(currentStepId + 1) ?? form.Steps.Last();

			// create the state
			return new FormState {
				FieldProperties = fieldProperties,
				FormInstanceProperties = formProperties,
				DataSource = dataSource,
				CurrentAction = action,
				IsPostback = !string.IsNullOrEmpty(action) || formProperties.Count > 0,
				CurrentStep = currentStep,
				NextStep = nextStep
			};
		}