Just for fun, I created my own custom error (message) handler that works pretty much as TempData, but with the small difference that this handler is accessible all over the application.
I'm not going to explain every single step of code, but to sum it all up, I used IHttpModule to fire a method for every request and Session to save data. Below is the code, feel free to edit or give suggestions for improvements.
Web.config (Define module)
<httpModules>
<add name="ErrorManagerModule" type="ErrorManagerNamespace.ErrorManager"/>
</httpModules>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ErrorManagerModule" type="ErrorManagerNamespace.ErrorManager"/>
</modules>
</system.webServer>
ErrorManager.cs (Error manager handler code)
public class ErrorManager : IRequiresSessionState, IHttpModule
{
private const string SessionKey = "ERROR_MANAGER_SESSION_KEY";
public enum Type
{
None,
Warning,
Success,
Error
}
/*
*
* Public methods
*
*/
public void Dispose()
{
}
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(Initiliaze);
}
public static IList<ErrorModel> GetErrors(ErrorManager.Type type = Type.None)
{
// Get all errors from session
var errors = GetErrorData();
// Destroy Keep alive
// Decrease all errors request count
foreach (var error in errors.Where(o => type == ErrorManager.Type.None || o.ErrorType == type).ToList())
{
error.KeepAlive = false;
error.IsRead = true;
}
// Save errors to session
SaveErrorData(errors);
//return errors;
return errors.Where(o => type == ErrorManager.Type.None || o.ErrorType == type).ToList();
}
public static void Add(ErrorModel error)
{
// Get all errors from session
var errors = GetErrorData();
var result = errors.Where(o => o.Key.Equals(error.Key, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
// Add error to collection
error.IsRead = false;
// Error with key is already associated
// Remove old error from collection
if (result != null)
errors.Remove(result);
// Add new to collection
// Save errors to session
errors.Add(error);
SaveErrorData(errors);
}
public static void Add(string key, object value, ErrorManager.Type type = Type.None, bool keepAlive = false)
{
// Create new error
Add(new ErrorModel()
{
IsRead = false,
Key = key,
Value = value,
KeepAlive = keepAlive,
ErrorType = type
});
}
public static void Remove(string key)
{
// Get all errors from session
var errors = GetErrorData();
var result = errors.Where(o => o.Key.Equals(key, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
// Error with key is in collection
// Remove old error
if (result != null)
errors.Remove(result);
// Save errors to session
SaveErrorData(errors);
}
public static void Clear()
{
// Clear all errors
HttpContext.Current.Session.Remove(SessionKey);
}
/*
*
* Private methods
*
*/
private void Initiliaze(object o, EventArgs e)
{
// Get context
var context = ((HttpApplication)o).Context;
// If session is ready
if (context.Handler is IRequiresSessionState ||
context.Handler is IReadOnlySessionState)
{
// Load all errors from session
LoadErrorData();
}
}
private static void LoadErrorData()
{
// Get all errors from session
var errors = GetErrorData().Where(o => !o.IsRead).ToList();
// If KeepAlive is set to false
// Mark error as read
foreach (var error in errors)
{
if (error.KeepAlive == false)
error.IsRead = true;
}
// Save errors to session
SaveErrorData(errors);
}
private static void SaveErrorData(IList<ErrorModel> errors)
{
// Make sure to remove any old errors
HttpContext.Current.Session.Remove(SessionKey);
HttpContext.Current.Session.Add(SessionKey, errors);
}
private static IList<ErrorModel> GetErrorData()
{
// Get all errors from session
return HttpContext.Current.Session[SessionKey]
as IList<ErrorModel> ??
new List<ErrorModel>();
}
/*
*
* Model
*
*/
public class ErrorModel
{
public string Key { get; set; }
public object Value { get; set; }
public bool KeepAlive { get; set; }
internal bool IsRead { get; set; }
public Type ErrorType { get; set; }
}
HtmlHelperExtension.cs (An extension method for rendering the errors)
public static class HtmlHelperExtension
{
public static string RenderMessages(this HtmlHelper obj, ErrorManager.Type type = ErrorManager.Type.None, object htmlAttributes = null)
{
var builder = new TagBuilder("ul");
var errors = ErrorManager.GetErrors(type);
// If there are no errors
// Return empty string
if (errors.Count == 0)
return string.Empty;
// Merge html attributes
builder.MergeAttributes(new RouteValueDictionary(htmlAttributes), true);
// Loop all errors
foreach (var error in errors)
{
builder.InnerHtml += String.Format("<li class=\"{0}\"><span>{1}</span></li>",
error.ErrorType.ToString().ToLower(),
error.Value as string);
}
return builder.ToString();
}
}
Usage for creating errors
// This will only be available for one request
ErrorManager.Add("Key", "An error message", ErrorManager.Type.Error);
// This will be available for multiple requests
// When error is read, it will be removed
ErrorManager.Add("Key", "An error message", ErrorManager.Type.Error, true);
// Remove an error
ErrorManager.Remove("AnotherKey");
// Clear all error
ErrorManager.Clear();
Usage for rendering errors
// This will render all errors
<%= Html.RenderMessages() %>
// This will just render all errors with type "Error"
<%= Html.RenderMessages(ErrorManager.Type.Error) %>