using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace System.Web.Mvc
{
///
/// Set of extensions methods on ControllerBase class that implements template method pattern for action method body.
/// The stages of action method execution are: view-model initialization, view-model update and action result obtaining.
///
public static class TemplateMethodExtensions
{
#region for GET action methods
///
/// Provides the right action method template for GET action method (action method that load ViewModel only).
/// ViewModel is automatically created using parameterless constructor and assigned into ViewData.Model property.
/// If some exception occurs during ViewModel initialization or update then this exception is added into ModelState.
///
/// ViewModel class that in used in this action method and related view.
/// Controller.
///
/// If some delegate is specified then allows to handle invalid ModelState after ViewModel initialization.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
///
/// Delegate that returns action result that will be returned from action method. If null then null is returned.
///
/// Returns action result.
/// This method only should be called inside action method.
public static ActionResult ProcessGET(this ControllerBase controller,
Func handleInvalidViewModelAfterInitialization,
Func obtainActionResult)
where TViewModel : new()
{
return ProcessGET(controller,
() => new TViewModel(),
handleInvalidViewModelAfterInitialization,
obtainActionResult);
}
///
/// Provides the right action method template for GET action method (action method that load ViewModel only).
/// If some exception occurs during ViewModel initialization or update then this exception is added into ModelState.
///
/// ViewModel class that in used in this action method and related view.
/// Controller.
///
/// Creates ViewModel instance. This would probably done using action method parameters. If null is specified then ViewModel will have its default value.
/// After model is created then it's assigned to ViewData.Model property.
///
///
/// If some delegate is specified then allows to handle invalid ModelState after ViewModel initialization.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
///
/// Delegate that returns action result that will be returned from action method. If null then null is returned.
///
/// Returns action result.
/// This method only should be called inside action method.
public static ActionResult ProcessGET(this ControllerBase controller,
Func initializeViewModel,
Func handleInvalidViewModelAfterInitialization,
Func obtainActionResult)
{
return ProcessGET(controller,
initializeViewModel,
(viewModel, exception) => { controller.ViewData.ModelState.AddModelError("error", exception); return null; },
handleInvalidViewModelAfterInitialization,
obtainActionResult,
null);
}
///
/// Provides the right action method template for GET action method (action method that load ViewModel only).
///
/// ViewModel class that in used in this action method and related view.
/// Controller.
///
/// Creates ViewModel instance. This would probably done using action method parameters. If null is specified then ViewModel will have its default value.
/// After model is created then it's assigned to ViewData.Model property.
///
///
/// If non-null is specified then catches all exceptions during ViewModel initialization.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
///
/// If some delegate is specified then allows to handle invalid ModelState after ViewModel initialization.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
///
/// Delegate that returns action result that will be returned from action method. If null then null is returned.
///
///
/// If non-null is specified then catches all exceptions during action result obtaining.
/// If some exception occurs then action method returns value that delegate returns.
///
/// Returns action result.
/// This method only should be called inside action method.
public static ActionResult ProcessGET(this ControllerBase controller,
Func initializeViewModel,
Func handleInitializeViewModelException,
Func handleInvalidViewModelAfterInitialization,
Func obtainActionResult,
Func handleObtainActionResultException)
{
return Process(controller,
initializeViewModel,
handleInitializeViewModelException,
handleInvalidViewModelAfterInitialization,
null,
null,
null,
obtainActionResult,
handleObtainActionResultException);
}
#endregion
#region for POST action methods
///
/// Provides the right action method template for action method that works on ViewModel (typically POST action methods).
/// ViewModel is automatically created using parameterless constructor and assigned into ViewData.Model property.
/// If some exception occurs during ViewModel initialization or update then this exception is added into ModelState.
///
/// ViewModel class that in used in this action method and related view.
/// Controller.
///
/// If some delegate is specified then allows to handle invalid ModelState after ViewModel initialization.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
/// Updates ViewModel.
///
/// If some delegate is specified then allows to handle invalid ModelState after ViewModel update.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
///
/// Delegate that returns action result that will be returned from action method. If null then null is returned.
///
/// Returns action result.
/// This method only should be called inside action method.
public static ActionResult Process(this ControllerBase controller,
Func handleInvalidViewModelAfterInitialization,
Action updateViewModel,
Func handleInvalidViewModelAfterUpdate,
Func obtainActionResult)
where TViewModel : new()
{
return Process(controller,
() => new TViewModel(),
handleInvalidViewModelAfterInitialization,
updateViewModel,
handleInvalidViewModelAfterUpdate,
obtainActionResult);
}
///
/// Provides the right action method template. Provides the right action method template for action method that works on ViewModel (typically POST action methods).
/// If some exception occurs during ViewModel initialization or update then this exception is added into ModelState.
///
/// ViewModel class that in used in this action method and related view.
/// Controller.
///
/// Creates ViewModel instance. This would probably done using action method parameters. If null is specified then ViewModel will have its default value.
/// After model is created then it's assigned to ViewData.Model property.
///
///
/// If some delegate is specified then allows to handle invalid ModelState after ViewModel initialization.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
/// Updates ViewModel.
///
/// If some delegate is specified then allows to handle invalid ModelState after ViewModel update.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
///
/// Delegate that returns action result that will be returned from action method. If null then null is returned.
///
/// Returns action result.
/// This method only should be called inside action method.
public static ActionResult Process(this ControllerBase controller,
Func initializeViewModel,
Func handleInvalidViewModelAfterInitialization,
Action updateViewModel,
Func handleInvalidViewModelAfterUpdate,
Func obtainActionResult)
{
return Process(controller,
initializeViewModel,
(viewModel, exception) => { controller.ViewData.ModelState.AddModelError("error", exception); return null; },
handleInvalidViewModelAfterInitialization,
updateViewModel,
(viewModel, exception) => { controller.ViewData.ModelState.AddModelError("error", exception); return null; },
handleInvalidViewModelAfterUpdate,
obtainActionResult,
null);
}
///
/// Provides the right action method template for action method that works on ViewModel (typically POST action methods).
///
/// ViewModel class that in used in this action method and related view.
/// Controller.
///
/// Creates ViewModel instance. This would probably done using action method parameters. If null is specified then ViewModel will have its default value.
/// After model is created then it's assigned to ViewData.Model property.
///
///
/// If non-null is specified then catches all exceptions during ViewModel initialization.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
///
/// If some delegate is specified then allows to handle invalid ModelState after ViewModel initialization.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
/// Updates ViewModel.
///
/// If non-null is specified then catches all exceptions during ViewModel update.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
///
/// If some delegate is specified then allows to handle invalid ModelState after ViewModel update.
/// If specified delegate returns non-null action result then current action method is terminated and this action result is used as return value.
///
///
/// Delegate that returns action result that will be returned from action method. If null then null is returned.
///
///
/// If non-null is specified then catches all exceptions during action result obtaining.
/// If some exception occurs then action method returns value that delegate returns.
///
/// Returns action result.
/// This method only should be called inside action method.
public static ActionResult Process(this ControllerBase controller,
Func initializeViewModel,
Func handleInitializeViewModelException,
Func handleInvalidViewModelAfterInitialization,
Action updateViewModel,
Func handleUpdateViewModelException,
Func handleInvalidViewModelAfterUpdate,
Func obtainActionResult,
Func handleObtainActionResultException)
{
if (controller == null)
{
throw new ArgumentNullException("controller");
}
var viewModel = default(TViewModel);
// ViewModel initialization
if (handleInitializeViewModelException != null)
{
try
{
if (initializeViewModel != null)
{
viewModel = initializeViewModel();
}
}
catch (Exception e)
{
var ar = handleInitializeViewModelException(viewModel, e);
if (ar != null)
{
return ar;
}
}
}
else
{
if (initializeViewModel != null)
{
viewModel = initializeViewModel();
}
}
// set view-ViewModel (but not rewrite)
if (controller.ViewData != null && controller.ViewData.Model == null)
{
controller.ViewData.Model = viewModel;
}
// post-initialize check
if (!controller.ViewData.ModelState.IsValid && handleInvalidViewModelAfterInitialization != null)
{
var res = handleInvalidViewModelAfterInitialization(viewModel);
if (res != null)
{
return res;
}
}
// ViewModel updating
if (handleUpdateViewModelException != null)
{
try
{
if (updateViewModel != null)
{
updateViewModel(viewModel);
}
}
catch (Exception e)
{
var ar = handleUpdateViewModelException(viewModel, e);
if (ar != null)
{
return ar;
}
}
}
else
{
if (updateViewModel != null)
{
updateViewModel(viewModel);
}
}
// post-update check
if (handleInvalidViewModelAfterUpdate != null)
{
var res = handleInvalidViewModelAfterUpdate(viewModel);
if (res != null)
{
return res;
}
}
if (handleObtainActionResultException != null)
{
try
{
return obtainActionResult != null ? obtainActionResult(viewModel) : null;
}
catch (Exception e)
{
return handleObtainActionResultException(viewModel, e);
}
}
else
{
return obtainActionResult != null ? obtainActionResult(viewModel) : null;
}
}
#endregion
}
}