Providing Intellisense, Navigation and more for Custom Helpers in ASP.NET MVC

You probably are aware by now that as of ReSharper 5, we added first-class support for ASP.NET MVC. This included among many things, the ability to provide Intellisense, Create from usage and Navigation to built-in methods such as Controller.View or Html.ActionLink:

Navigation

Ctrl+Left Mouse Click or F12 will navigate to the corresponding View

image

or to the Action and/or Controller

image

Intellisense and Create From Usage

Ability to have Intellisense when providing Actions/Controllers

image

as well as the possibility of creating from usage

image

However, what happens when you want to use a custom function, for instance, a better ActionLink or your own View method? Did you know that you can still get all these goodies? All you need to do is use some Annotations.

Using JetBrains.Annotations

ReSharper uses annotations via the form of .NET attributes to figure out what an ASP.NET MVC View, Action or Controller is. As such, all we need to do for our custom method and extensions to leverage this, is tell ReSharper what parameter corresponds to what.

Referencing the annotations

To use ReSharper annotations, we have mainly two options (with a third one hopefully coming soon):

1. We can include the library JetBrains.Annotations.dll in our project and reference it.

2. We can copy the annotations and include it as source in our project

[3. We can use nuget install-package JetBrains.Annotations] Coming soon!

The first option is pretty simple. The DLL is located in the ReSharper installation bin folder. For the second option, we open up ReSharper | Options and select Code Annotations entry

image

select the Copy default implementation to clipboard button and paste into an empty file.

Annotating custom methods

Once we’ve completed this step, all we need to do is annotate our parameters with the correct attributes. We’re interested in 3 different attributes in particular:

  • AspMvcView which indicates the parameter is a View
  • AspMvcAction which indicates the parameter is an Action
  • AspMvcController which indicates the parameter is a Controller

Here is the header corresponding to a base controller with a custom method named ExtendedView

image

and here’s the header for a custom ActionLink

image

(the body of both methods are omitted and are not necessary to demonstrate the functionality)

As soon as we do this, ReSharper picks up these methods and offers us the same functionality that is provided for the methods that ship out of the box:

image

Notice how we still get Navigation (the underlining), Intellisense and Create from usage in our TheOnlyActionLink custom method. Its much the same for the ExtendedView method

image

That’s all there is to it.

This entry was posted in News and Events and tagged . Bookmark the permalink.

14 Responses to Providing Intellisense, Navigation and more for Custom Helpers in ASP.NET MVC

  1. Daniel White says:

    What about for RouteNames?

  2. Daniel White says:

    Also, where can I download it the source for these annotations?

  3. Hadi Hariri says:

    @Daniel,

    What is it you need to do with RouteNames? The source code for the annotations, as highlighted in the post is actually available via ReSharper | Options. See “Referencing the annotations” section.

  4. Carles Company Soler says:

    Just the other day I wrote an answer in StackOverflow regarding this. I’ll have to update it! http://stackoverflow.com/questions/8371103/magic-strings-in-asp-net-mvc/8371241#8371241

  5. Matt Hinze says:

    Is there a facility to support custom view search paths?

  6. @Matt, if you use standard view engine and altering ViewLocationFormats or other location properties – it’s already there!
    ReSharper detects that and use new location formats.

  7. Konstantin says:

    How can I disable razor processing? It takes ages to process all views and r# reprocesses them on solution open.

  8. Matt Hinze says:

    @Slava – we build tooling for other dev teams in our company, and we do something like this in the base application class:

    foreach (IViewEngine x in ViewEngines.Engines) {
    var wbe = x as WebFormViewEngine;
    if (wbe != null) {
    // append to wbe.MasterLocationFormats
    // append to wbe.viewLocationFormats
    // etc
    }
    var rve = x as RazorViewEngine;
    if (rve != null) {
    // apend to rve.masterLocationFormats
    // append to rve.viewlocationformats
    //etc
    }
    }

    Is there a way to use annotations or build a descriptor that resharper can consume from a run time view engine?

  9. derigel says:

    Matt, there is no such descriptor currently.
    Altrough, ReSharper should detect these modifications as you provided, but only in source code.
    If you modify them in base application and reference that application binary – R# will miss that.

  10. Matt Hinze says:

    @derigel Thanks, I will take another look!

  11. Carles Company Soler says:

    Is there a way to use this in object properties? I he a model that represents my menu and some of its properties are Action, Controller and Area. If I could decorate this properties with these annotations it would be great.

  12. @Carles, nope, currently these attributes are only recognized on arguments or methods.

  13. Keith Barrow says:

    We have a situation where want to specify the action as a parameter, but not the controller (which in our case is always going to be our ErrorPageController). Something like:
    [AspMvcController("ErrorPage")] //All use ErrorPageController for method
    public ActionResult RedirectToErrorPageAction([AspMvcAction] action, ……)
    {
    //snip
    }

    • Matt Ellis says:

      Hi Keith. If I understand what you’re after correctly, the annotations don’t work like this right now – the string parameter passed to AspMvcControllerAttribute is the name to use if the parameter it’s applied to is an anonymous type. For example:


      public ActionResult Foo([AspMvcController("Controller")] object values)

      would pick the controller out of the anonymous type:


      Foo(new { Controller = "Home" });

      When the attribute is applied to the method, the controller name is inferred from the surrounding context, such as location of view, or containing class name.

      I’ve added a feature request to be able to specify a default controller name with the attribute. Please take a look and see if it matches what you’re after, and feel free to comment and vote: http://youtrack.jetbrains.com/issue/RSRP-406269

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>