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

27

Я пытаюсь создать собственный 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);
    }
}
  • 0
    Ваш вопрос супер, но решение гораздо более удивительное, чем вопрос. Я искал решение для отправки параметров в глобальные атрибуты фильтра. Это болеутоляющее для меня :)
Теги:
asp.net-mvc
action-filter

4 ответа

30
Лучший ответ

Это способ сделать эту работу. У вас есть доступ к 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 с общими свойствами и просто отбрасывание на него.

  • 1
    Ну, это приближает меня к решению. спасибо Однако actionFilter будет использоваться на нескольких контроллерах.
  • 0
    У меня много проблем с получением контроллера. var thisController = ((MemberController)ControllerContext.Controller); возвращает ошибку: An object reference is required for the non-static field, method, or property 'System.Web.Mvc.ControllerContext.Controller.get'
Показать ещё 7 комментариев
8

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

3

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

Для этого вы должны изучить "Инъекции зависимостей для Action Action", обычно используя контейнер IoC.

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

public ActionResult Index()
{
  ControllerContext.RouteData.DataTokens.Add("name", "value");
  return View();
}
  • 0
    +1 для предоставления дальнейших указаний ... инъекция зависимости является идеальным решением в этом случае; расположение службы - еще один (более простой) вариант, который следует учитывать.
  • 0
    Ну ... я не уверен, что это возможно тогда. md.memberGUID привязан к сеансу и не может быть const, потому что сайту потребуется доступ для обновления данных для входа.
Показать ещё 6 комментариев
-5

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

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню