Как передать параметры пользовательскому ActionFilter в ASP.NET MVC 2?

Я пытаюсь создать собственный ActionFilter, который работает с набором параметров, которые будут переданы ему из контроллера.

Пока что мой клиент ActionFilter выглядит так:

public class CheckLoggedIn : ActionFilterAttribute
{
    public IGenesisRepository gr { get; set; }
    public Guid memberGuid { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Member thisMember = gr.GetActiveMember(memberGuid);
        Member bottomMember = gr.GetMemberOnBottom();

        if (thisMember.Role.Tier <= bottomMember.Role.Tier)
        {
            filterContext
                .HttpContext
                .Response
                .RedirectToRoute(new { controller = "Member", action = "Login" });
        }

        base.OnActionExecuting(filterContext);
    }
}

Я знаю, что мне все еще нужно проверять наличие нулей и т. Д., Но я не могу понять, почему gr а также memberGuid не были успешно переданы. Я называю этот фильтр следующим образом:

    [CheckLoggedIn(gr = genesisRepository, memberGuid = md.memberGUID)]
    public ActionResult Home(MemberData md)
    {
        return View(md);
    }

genesisRepository а также md устанавливаются в конструкторе контроллера.

Я не могу заставить это скомпилировать. Я получаю ошибку:

Error   1   'gr' is not a valid named attribute argument because it is not a valid attribute parameter type
Error   2   'memberGuid' is not a valid named attribute argument because it is not a valid attribute parameter type

Я дважды проверил это gr а также memberGuid были такие же типы, как genesisRepority а также md.memberGUIDЧто вызывает эти ошибки?

Решение

Спасибо jfar за предложенное решение.

Вот фильтр, который я в итоге использовал:

public class CheckLoggedIn : ActionFilterAttribute
{

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var thisController = ((MemberController)filterContext.Controller);

        IGenesisRepository gr = thisController.GenesisRepository;
        Guid memberGuid = ((MemberData)filterContext.HttpContext.Session[thisController.MemberKey]).MemberGUID;

        Member thisMember = gr.GetActiveMember(memberGuid);
        Member bottomMember = gr.GetMemberOnBottom();

        if (thisMember.Role.Tier >= bottomMember.Role.Tier)
        {
            filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary(
                    new { 
                        controller = "Member", 
                        action = "Login" 
                    }));
        }

        base.OnActionExecuting(filterContext);
    }
}

3 ответа

Решение

Это способ заставить эту работу. У вас есть доступ к ControllerContext и, следовательно, Controller из объекта ActionFilter. Все, что вам нужно сделать, это привести ваш контроллер к типу, и вы сможете получить доступ к любым публичным членам.

Учитывая этот контроллер:

public GenesisController : Controller
{
    [CheckLoggedIn()]
    public ActionResult Home(MemberData md)
    {
        return View(md);
    }
}

ActionFilter выглядит примерно так

public class CheckLoggedIn : ActionFilterAttribute
{
    public IGenesisRepository gr { get; set; }
    public Guid memberGuid { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        /* how to get the controller*/
        var controllerUsingThisAttribute = ((GenesisController)filterContext.Controller);

        /* now you can use the public properties from the controller */
        gr = controllerUsingThisAttribute .genesisRepository;
        memberGuid = (controllerUsingThisAttribute .memberGuid;

        Member thisMember = gr.GetActiveMember(memberGuid);
        Member bottomMember = gr.GetMemberOnBottom();

        if (thisMember.Role.Tier <= bottomMember.Role.Tier)
        {
            filterContext
                .HttpContext
                .Response
                .RedirectToRoute(new { controller = "Member", action = "Login" });
        }

        base.OnActionExecuting(filterContext);
    }
}

Конечно, это предполагает, что ActionFilter не используется на нескольких контроллерах, и вы согласны с соединением. Другой вариант - создать интерфейс ICheckedLoggedInController с общими свойствами и просто привести его к этому.

Вы можете использовать только постоянные значения для свойств атрибута; см. эту страницу для полного объяснения.

Атрибуты по сути являются метаданными, добавляемыми к типу. Они могут использовать только const значения, а не переменные экземпляра. В вашем случае вы пытаетесь передать переменные вашего экземпляра genisisRepositoryи т.д. Это не удастся скомпилировать, так как они не являются постоянными времени компиляции.

Чтобы добиться этого, вам нужно изучить внедрение зависимостей для фильтров действий, обычно используя контейнер IoC.

Кроме того, если ваш Action Filter выполняет действие после ActionResult, например OnActionExecutedвы могли бы избежать хранения чего-либо в данных маршрута:

public ActionResult Index()
{
  ControllerContext.RouteData.DataTokens.Add("name", "value");
  return View();
}

Инъекция зависимости является идеальным решением в этом случае; Расположение службы - еще один (более простой) вариант, который следует учитывать.

Ну... я не уверен, что это возможно тогда. md.memberGUID привязан к сеансу и не может быть константным, поскольку сайту потребуется доступ для обновления данных для входа.

потому что я не хочу создавать зависимость от конкретного сеансового ключа. Я бы предпочел, чтобы проверка global.aspx.cs выявила привязку, и будущий разработчик мог легко внести изменения

Добавление в данные маршрута данных, которых нет в URL, является довольно ужасным способом хранения данных. Это приводит к трудно найти ошибки. - Внедрение зависимости не помогает решить эту проблему. Любая техника, которая предоставляет репозитории для ActionFilter, будет работать. Теперь, конечно, DI лучше помочь остановить проблемы с расположением сервисов, но это НЕ решение, а всего лишь один из способов активировать решение. Хорошая статья о том, как этого добиться: lostechies.com/blogs/jimmy_bogard/archive/2010/05/03/…

Другие вопросы по тегам