Снова возникла задача во время разработки, решением которой хочу поделиться. Разрабатываю панель администрирования одного сайта. И как обычно это бывает, нужно отобразить разного рода данные из базы
в таблицах на странице. Так как данных много, то они отображаются с разбиением на страницы. Причём у пользователя есть возможность настраивать количество, фильтрацию и т.п. Естественно, все эти настройки передаются как параметры метода действия контроллера. Задача была следующая: сохранить их (настройки) во время сеанса. В ASP.NET WebForms за нас это всё, ну или почти всё, делается автоматически. Не думая, решение было следующее.
public ViewResult Catalogs(ushort? page, ushort? rwcnt, string sort)
{
string controllerName = RouteData.Values["controller"].ToString();
if (page == null)
{
if (Session["pageNumber" + controllerName] == null)
{
page = 1;
Session.Add("pageNumber" + controllerName, page);
}
else
page = ushort.Parse(Session["pageNumber" + controllerName].ToString());
}
else
{
Session.Add("pageNumber" + controllerName, page);
}
if (rwcnt == null)
{
if (Session["rowCount" + controllerName] == null)
{
rwcnt = 40;
Session.Add("rowCount" + controllerName, rwcnt);
}
else
rwcnt = ushort.Parse(Session["rowCount" + controllerName].ToString());
}
else
{
Session.Add("rowCount" + controllerName, rwcnt);
}
if (sort == null)
{
if (Session["sortParam" + controllerName] == null)
{
sort = "Default";
Session.Add("sortParam" + controllerName, page);
}
else
sort = Session["sortParam" + controllerName].ToString();
}
else
{
Session.Add("sortParam" + controllerName, page);
}
ViewData.Model = _repository.GetCatalogs(page, rwcnt, sort, true);
return View();
}
Какой громоздкий код получился, правда? А учитывая, что таких методов очень много, придётся неоднократно копировать данный код. В первую очередь это не очень удобно и невыразительно. Как обычно, решил покопаться в интернете. Ничего нормального не найдя, начал думать сам. В итоге получилось такое компактное решение.
public class ActionParametersCacheAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Получаем ссылку на объект сессии.
HttpSessionStateBase session = filterContext.HttpContext.Session;
//Получаем ссылку на коллекцию параметров.
IDictionary<string, object> parameters = filterContext.ActionParameters;
//Создаём отдельный массив для ключей.
string[] keys = parameters.Keys.ToArray();
//Получаем имя контроллера.
string controllerName = filterContext.RouteData.Values["controller"].ToString();
//Обходимся без цикла foreach, так как может понадобиться модификация коллекции.
for (int i = 0; i < keys.Length; i++)
{
//Если параметр поступил, добавляем её в сессию.
if (parameters[keys[i]] != null)
{
/*Как ключ, устанавливаем имя параметра + имя контроллера, если нужно,
можно и имя действия тоже. Так как таблиц много, нужно чтобы для каждой
была индивидуальная настройка.*/
session.Add(keys[i] + controllerName, parameters[keys[i]]);
}
else
{
//Если он есть в сессии, заменяем. Иначе, ничего не делаем.
if (session[keys[i] + controllerName] != null)
{
parameters.Remove(keys[i]);
parameters.Add(keys[i], session[keys[i] + controllerName]);
}
}
}
base.OnActionExecuting(filterContext);
}
}
Наследуемся от стандартного атрибута (название фильтры я не люблю) ActionMethodAttribute, переопределаям его метод OnActionExecuting (который выполняется до метода действия контроллера), и делаем то, что нам нужно. Не нужно будет заниматься копированием, а так как это атрибут, достаточно применить его к тем методам, которые нам нужны.
[ActionParametersCache]
public ViewResult Catalogs(ushort? page, ushort? rwcnt, string sort)
{
ViewData.Model = _repository.GetCatalogs(page ?? 1, rwcnt ?? 40, sort, true);
return View();
}
Хотя название ActionParametersCacheAttribute не совсем точное, но пока ничего лучше не смог придумать.