using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace System.Web.Mvc
{
///
/// Provides the right action method template for action method.
///
/// ViewModel class that in used in this action method and related view.
public class ActionTemplate
{
///
/// Initializes a new instance of ActionTemplate class.
///
/// The controller.
/// If true then if some exception occurs during ViewModel initialization or update then this exception is added into ModelState.
public ActionTemplate(ControllerBase controller, bool saveExceptionsToModelState)
{
this.Controller = controller;
if (saveExceptionsToModelState)
{
this.HandleInitializeViewModelException = (viewModel, exception) => { this.Controller.ViewData.ModelState.AddModelError("error", exception); return null; };
this.HandleUpdateViewModelException = (viewModel, exception) => { this.Controller.ViewData.ModelState.AddModelError("error", exception); return null; };
}
}
///
/// The controller.
///
public ControllerBase Controller
{
get;
set;
}
#region initialization
///
/// 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.
///
public Func InitializeViewModel { get; set; }
///
/// 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.
///
public Func HandleInitializeViewModelException { get; set; }
///
/// 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.
///
public Func HandleInvalidViewModelAfterInitialization { get; set; }
#endregion
#region update
///
/// Updates ViewModel.
///
public Action UpdateViewModel { get; set; }
///
/// 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.
///
public Func HandleUpdateViewModelException { get; set; }
///
/// 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.
///
public Func HandleInvalidViewModelAfterUpdate { get; set; }
#endregion
#region result
///
/// Delegate that returns action result that will be returned from action method. If null then null is returned.
///
public Func ObtainActionResult { get; set; }
///
/// 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.
///
public Func HandleObtainActionResultException { get; set; }
#endregion
///
/// Runs delegates specified as properties in the right order.
///
/// Returns action result.
public virtual ActionResult Run()
{
if (this.Controller == null)
{
throw new InvalidOperationException("Controller property must be set before Run method calling.");
}
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;
}
}
}
///
/// Provides the right action method template for action method.
/// The ViewModel type must have parameterless constructor. This constructor is called by automatically generated InitializeViewModel delegate.
///
/// ViewModel class that in used in this action method and related view.
public class ActionTemplateWithInstantiableViewModel : ActionTemplate
where TViewModel : new()
{
///
/// Initializes a new instance of ActionTemplateWithInstantiableViewModel class.
///
/// The controller.
/// If true then if some exception occurs during ViewModel initialization or update then this exception is added into ModelState.
public ActionTemplateWithInstantiableViewModel(ControllerBase controller, bool saveExceptionsToModelState)
: base(controller, saveExceptionsToModelState)
{
this.InitializeViewModel = () => new TViewModel();
}
}
}