Как передать параметры пользовательскому ActionFilter в ASP.NET MVC 2? - PullRequest
31 голосов
/ 03 декабря 2010

Я пытаюсь создать собственный 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 ]

33 голосов
/ 03 декабря 2010

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

8 голосов
/ 03 декабря 2010

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

3 голосов
/ 03 декабря 2010

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

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

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

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