C # MVC sitemap - very slow when using roles - very slow

I installed MVC Site Map Provider for MVC5 and just used everything out of the box. It works fine. Now I want to implement role-based menu clipping, so assuming my controller is:

public class Home: Controller
{

    [Authorize(Roles="Admin")]
    public ActionResult Index()
    {
        return View();
    }
}

      

Mainly Admin users can now see the menu. Perfect works great.

Also to implement this, I added this line to my web.config:

  <add key="MvcSiteMapProvider_SecurityTrimmingEnabled" value="true" />

      

The problem is that it works, but it is slow. It takes about 7 seconds to load the page. If I remove the web.config line, basically removing the role based menu clipping takes ~ 300ms for the page to load. Something is wrong here.

Any ideas why my role-based menu setup is slow? I didn't make any settings.

0


source to share


2 answers


Although there is a bug noted for Route values ​​not saved correctly in v4?

But it looks like it was fixed in version 4 of the next release.



Another workaround to fix this issue is cache, this is the linked article.

MVC provider key siteMap

0


source


The security trimming feature relies on instantiating a controller for each node to determine if the current user context is available.

The most likely reason for this slowness is that your controllers (or their base class) have too much constructor processing.

public class HomeController
{
    public HomeController() {
        // Lots of heavy processing
        System.Threading.Thread.Sleep(300);
    };
}

      

The above example will add 300ms to the page load time for each node that represents an action method in HomeController

. If your other controllers also have heavy handling during instantiation, they will also add extra time for each page load.

This is not a problem with DI best practices because there is a lot of processing going on in external services when the controller is instantiated.

public interface IHeavyProcessingService
{
    IProcessingResult DoSomethingExpensive();
}

public class HeavyProcessingService : IHeavyProcessingService
{
    public HeavyProcessingService() { 
    }

    public IProcessingResult DoSomethingExpensive() {
        // Lots of heavy processing
        System.Threading.Thread.Sleep(300);
    }
}

public class HomeController
{
    private readonly IHeavyProcessingService heavyProcessingService;

    // The constructor does no heavy processing. It is deferred until after
    // the instance is created by HeavyProcessingService. 
    // The only thing happening here is assignment of dependencies.
    public HomeController(IHeavyProcessingService heavyProcessingService) {

        if (heavyProcessingService == null)
            throw new ArgumentNullException("heavyProcessingService");

        this.heavyProcessingService = heavyProcessingService;
    };

    public ActionResult Index()
    {
        var result = this.heavyProcessingService.DoSomethingExpensive();

        // Do something with the result of the heavy processing

        return View();
    }

    public ActionResult About()
    {
        return View();
    }

    public ActionResult Contact()
    {
        return View();
    }
}

      



Note that there was no heavy handling in the constructor in the above example? This means that instantiation is HomeController

very cheap. This also means that action methods that do not require heavy handling (such as in About()

and Contact()

in the example) will not take the hit of heavy handling required Index()

.

Without using DI, MVC still requires a new controller instance to be created for every request (controller instances are never shared between users or action methods). Although in this case it is not so noticeable for each user, because only 1 instance is created for each user. Basically, it is MvcSiteMapProvider

slowing down due to an existing problem with your application (which you can now fix).

Even if you are not using DI, it is still best to defer heavy processing until the controller is instantiated.

public class HomeController
{
    private readonly IHeavyProcessingService heavyProcessingService;

    public HomeController() {

        this.heavyProcessingService = new HeavyProcessingService();
    };

    public ActionResult Index()
    {
        var result = this.heavyProcessingService.DoSomethingExpensive();

        // Do something with the result of the heavy processing

        return View();
    }
}

      

But if moving heavy processing to external services in your application is not an option, you can still defer processing until you need it by moving the processing to a different method, so instantiating the controller isn't too expensive.

public class HomeController
{
    public HomeController() {
    };

    private IProcessingResult DoSomethingExpensive() {
        // Lots of heavy processing
        System.Threading.Thread.Sleep(300);
    }

    public ActionResult Index()
    {
        var result = this.DoSomethingExpensive();

        // Do something with the result of the heavy processing

        return View();
    }
}

      

0


source







All Articles