5

I'm using attribute routing in MVC 5, but I've began to notice a bit of a pain point. I have a situation where I want to subclass a controller because all the actions are going to do the same thing. However, I will obviously need to use different routes for the subclasses' actions. Right now, I'm doing something akin to:

public class FooController : Controller
{
    [Route("foo", Name = "Foo")]
    public virtual ActionResult Foo()
    {
        ...

        return View();
    }
}

public class BarController : FooController
{
    [Route("bar", Name = "Bar")]
    public override ActionResult Foo()
    {
        return base.Foo();
    }
}

This just seems awful to me. I'm not repeating the code for the actual action method (which in some cases is quite a lot), at least, but this just feels wrong to me. Also, in situations where the base action method definition changes for some reason, this becomes a bit of a maintenance nightmare. Is there some way I'm missing, in general, to change the attribute without having to override the method? Perhaps, something attribute routing-specific. Or am I just kind of out of luck?

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • 4
    I'd think you are out of luck. Since attributes are defined in the assembly metadata, if you don't have a "stub" method to decorate with an attribute, you can't change it. This may be a case where using the standard routing mechanism would be better than using attribute routing. – Nathan A Aug 08 '14 at 17:00
  • I've deleted my answer and agree with Nathan on this. – jamesSampica Aug 13 '14 at 14:06

2 Answers2

0

Check out this SO post and answer

Multiple RoutePrefixes per controller using MVC Attribute routing?

You could do something like this then:

[RoutePrefix("{Type:regex(Foo|Bar)}")]
public class FooController : Controller
{
    [Route("foo", Name = "Foo")]
    public virtual ActionResult Foo()
    {
        ...

        return View();
    }
}
Community
  • 1
  • 1
jeff.eynon
  • 1,296
  • 3
  • 11
  • 29
  • Thanks for the attempt, but that doesn't solve the issue. In fact, it doesn't really do anything that having a regular old route prefix on each individual controller would do. In truth, the only part of the route attribute that causes problems is the `Name`, since if I merely subclass the controller, I'll get an error that there's multiple routes named `Foo`. If I don't name any routes, it would actually work fine as long as I was cool with the same route part for each action in each controller, but I actually do need to name the routes in my scenario. – Chris Pratt Aug 15 '14 at 14:48
0

I know I'm a million years late to the party, but I would have abstracted away the common code between the two to an abstract controller class, and then inherited that class on both your Foo and Bar controllers. Then you could either override the methods that need to be different for each controller and giving them an attribute in the derived controllers, or not put the method that would need to be overridden in the base controller class to begin with. Regardless though, I probably would have gone the Routing.config way for specifying route names and such as opposed to the Attribute way.

EDIT

What you had

public class FooController : Controller
{
    [Route("foo", Name = "Foo")]
    public virtual ActionResult Foo()
    {
        ...

    return View();
    }
}

public class BarController : FooController
{
    [Route("bar", Name = "Bar")]
    public override ActionResult Foo()
    {
        return base.Foo();
    }
}

What I'm suggesting

public abstract class _baseController : Controller
{
    public ActionResult ActionThatDoesNotNeedToBeOverridden()
    {
        ...
        return View();
    }

    public virtual ActionResult Foo()
    {
        ...
        return View();
    }
}

public class FooController : _baseController
{
    [Route("foo", Name = "Foo")]
    public override ActionResult Foo()
    {
        return base.Foo();
    }
}

public class BarController : _baseController
{
    [Route("bar", Name = "Bar")]
    public override ActionResult Foo()
    {
        return base.Foo();
    }
}

EDIT 2 You could do this if you don't want to override or bother with routing

public class _baseController : Controller
{
    public ActionResult Foo()
    {
        ...
        return View();
    }
}

public class BarController : Controller
{
    public ActionResult Bar()
    {
        var b = new _baseController();
        return b.Foo();
    }
}
Kevin Heidt
  • 1,135
  • 1
  • 11
  • 14
  • Sorry, but I'm not sure how that's any different than what I'm doing. The code is abstracted away in an abstract base controller. The problem is that I have to still override the method, if only to just call the base method, just to apply the new attribute. True, standard routing side-steps the problem, but the whole question is about getting around this issue, *while still using attribute routing*. – Chris Pratt Oct 27 '15 at 13:01
  • I think my point is that you wouldn't have any routing attributes in the base class, but would instead only apply them in the derived FooController and BarController. That way the overridden methods in each of those controllers will ultimately only have one set of routing attributes attached to them. – Kevin Heidt Oct 27 '15 at 17:25
  • That wasn't my issue. Actually, that's what I already have. The base class has no route attributes. The problem is with having to override the actions at all, when they just merely just call the base method, just to apply a route attribute to it. – Chris Pratt Oct 28 '15 at 14:10
  • I must be confused then. Your code sample has FooController with method Foo which has a route attribute, then it has BarController which inherits FooController and then overrides method Foo and has another route attribute. So your Foo method in BarController actually has 2 route attributes on it. I'll update my answer with an edit that shows what I'm talking about, and you tell me if what you have is the same. – Kevin Heidt Oct 29 '15 at 15:02
  • Updated my answer, let me know if I'm mistaken – Kevin Heidt Oct 29 '15 at 15:12
  • Sorry. Yeah, we're talking around each other a bit. My sample code was a little contrived, and I did show the attribute on the base class. However, that was never the true issue. The problem I have is in having to override the method for no other reason than apply the attribute. Your code doesn't do anything to aid that. – Chris Pratt Oct 29 '15 at 15:13
  • Ahhh... in that case check out my latest edit as a possible solution **NOTE** that I removed the abstract declaration for the _baseController so that it could be instantiated. Also BarController no longer inherits _baseController. – Kevin Heidt Oct 29 '15 at 15:38
  • You could also just have the method Bar without routing attributes but still call the base.Foo I believe? Don't quote me on that though. – Kevin Heidt Oct 29 '15 at 15:42