Cannot Resolve view using MVC areas

Hello All,

 

Using R# 2017.2, Windows 10, VS 2017 15.4.5.

 

The basic issue is illustrated here:

 

R# is saying it is unable to resolve the view "QuickSearch.cshtml", even though it is present in a folder that VS has no trouble resolving (i.e. running the app, the view renders fine).

The route is registered in a standard AreaRegistration context:

Snippet

public class ShopAreaRegistration : AreaRegistration
{
    public override string AreaName => "shop";

    public override void RegisterArea(AreaRegistrationContext context)     { // Other routes redacted
            context.MapDesktopAndMobileRoute(                 "shop_quick_search",                 "shop/quick-search/",                 new { controller = "Shop", action = "QuickSearch", type = "ci" }             );
}
}

/// Separate static class for these extension methods below

public static void MapDesktopAndMobileRoute(this AreaRegistrationContext context, string name, string url, object defaults) {     MapDesktopAndMobileRoute(context, name, url, defaults, null, null); }

public static void MapDesktopAndMobileRoute(this AreaRegistrationContext context, string name, string url, object defaults, object constraints, string[] namespaces)
{
    try
    {
        if (MobileIsEnabled)
        {
        MapMobileRoute(context, name, url, defaults, constraints, namespaces);
        }

        MapDesktopRoute(context, name, url, defaults, constraints, namespaces);
}
    catch (Exception ex)
    {
        log.Error($"Error mapping Desktop & Mobile Route for {name} - {url}.", ex);
    }
}

public static void MapDesktopAndMobileRoute(this AreaRegistrationContext context, string name, string url, object defaults)
{
MapDesktopAndMobileRoute(context, name, url, defaults, null, null);
}

public static void MapDesktopAndMobileRoute(this AreaRegistrationContext context, string name, string url, object defaults, object constraints, string[] namespaces)
{
    try
    {
        if (MobileIsEnabled)
        {
        MapMobileRoute(context, name, url, defaults, constraints, namespaces);
        }

        MapDesktopRoute(context, name, url, defaults, constraints, namespaces);
}
    catch (Exception ex)
    {
        log.Error($"Error mapping Desktop & Mobile Route for {name} - {url}.", ex);
    }
}

private static Route MapDesktopRoute(AreaRegistrationContext context, string name, string url, object defaults, object constraints, string[] namespaces) {     string routeName = DESKTOP_NAME_PREFIX + name;     Route route = context.MapRoute(routeName, url.StripLeadingSlash(), defaults, constraints, namespaces);     route.Constraints.Add("siteCheck", new DesktopRouteConstraint());     return route; } private static Route MapMobileRoute(this AreaRegistrationContext context, string name, string url, object defaults, object constraints, string[] namespaces) {     string routeName = MOBILE_NAME_PREFIX + name;     Route route = context.MapRoute(routeName, MOBILE_PATH_PREFIX + url.StripLeadingSlash(), defaults, constraints, namespaces);     route.DataTokens["mobile"] = true;     route.Constraints.Add("siteCheck", new MobileRouteConstraint());     return route; }
 

This only seems to happen when the view is *only* located in in a skinned theme area.   If there's a main view in the regular views folder, and an overriding view in the themes folder, R# doesn't complain, likely because it sees the one in the regular views folder, e.g.

 

How can I get R# to recognize what VS 17 does -- that the view or layout can exist in either the standard views location within the area, or the skinned theme location?   I do not want to place these views in the standard areas\shop\views\Shop folder, because they do not apply to all skins of the site -- only this specific one.

Happy to provide web.config or .csproj files upon request, or to generate a R# debug log as needed.

Thanks!

Jeff Woods

Reading, PA

 

0
3 comments
Avatar
Permanently deleted user

Here's a separate manifestation that also describes the problem:

The following views exist in the solution:

       ~\areas\shop\Views\Shop\Search.cshtml

This one has a model type of ISearchModel, and is the one that the Csm action uses.

        ~\theme\cbid\views\shop\Shop\Shop\Search.cshtml

This one has a model type of ElasticSearchModel (a new search engine the project has only recently begun using, for one theme only), which is the one that the ElasticIndex action should use.

When the compiled version runs on IIS, the correct view is rendered -- Csm uses the one in areas, and ElasticIndex renders the one in themes.

As you can see on line 163 above, however, R# is reporting a model type incompatibility.  It is saying "Argument type ElasticSearchModel is not assignable to model type ISearchModel".

R#'s suggestions to change the type would break the proper function of the code.  About all I can do here is disable the warning, and I hate littering code with disable/enable for single lines, so I tend to do it for an entire class, or to disable the inspection.... which I'd rather not.

Thanks again for looking!

Jeff Woods

Reading, PA

 

 

 

 

 

 

0

Jeff, how do you register your theme views resolving? Seems, it's not standard folder and by default it's not handled as you described. There is some additional logic - please, share it. Or share your solution - we can sign NDA.

0
Avatar
Permanently deleted user

Hello Slava,

RazorViewEngine has been overridden to allow for this.

Snippet

public class AtlasViewEngine : RazorViewEngine
{
    public AtlasViewEngine()
    {
        AreaViewLocationFormats = new[] {
            "~/theme/" + AtlasData.Settings.Theme.Name + "/views/{2}/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.cshtml"
        };
        AreaMasterLocationFormats = new[] {
            "~/theme/" + AtlasData.Settings.Theme.Name + "/views/{2}/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.cshtml"
        };
        AreaPartialViewLocationFormats = new[] {
            "~/theme/" + AtlasData.Settings.Theme.Name + "/views/{2}/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/Shared/{0}.cshtml"
        };
 
        ViewLocationFormats = new[] {
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml",
            "~/Areas/Shared/Views/{0}.cshtml"
        };
        MasterLocationFormats = new[] {
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml"
        };
        PartialViewLocationFormats = new[] {
            "~/Views/{1}/{0}.cshtml",
            "~/Views/Shared/{0}.cshtml",
            "~/Areas/Shared/Views/{0}.cshtml"
        };
 
        FileExtensions = new[] {
            "cshtml"
        };
    }
 
    [DebuggerStepThrough]
    public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
    {
        ViewEngineResult result;
 
        if (MobileHelper.IsMobileRequest(controllerContext.HttpContext.Request) && AtlasData.Settings.Mobile.IsEnabled)
        {
            string mobileViewName = partialViewName.EndsWith(".cshtml") ? partialViewName.Replace(".cshtml", ".mobile.cshtml") : (partialViewName + ".mobile");
 
            result = base.FindPartialView(controllerContext, mobileViewName, false /*useCache*/);
 
            if (result?.View == null)
            {
                result = base.FindPartialView(controllerContext, partialViewName, useCache);
            }
        }
        else
        {
            result = base.FindPartialView(controllerContext, partialViewName, useCache);
        }
 
        return OutputViewPath(result);
    }
 
    [DebuggerStepThrough]
    public override ViewEngineResult  FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        ViewEngineResult result;
 
        if (MobileHelper.IsMobileRequest(controllerContext.HttpContext.Request) && AtlasData.Settings.Mobile.IsEnabled)
        {
            string mobileViewName = viewName.EndsWith(".cshtml") ? viewName.Replace(".cshtml", ".mobile.cshtml") : (viewName + ".mobile");
 
            result = base.FindView(controllerContext, mobileViewName, masterName, false /*useCache*/);
 
            if (result?.View == null)
            {
                result = base.FindView(controllerContext, viewName, masterName, useCache);
            }
        }
        else
        {
            result = base.FindView(controllerContext, viewName, masterName, useCache);
        }
 
        return OutputViewPath(result);
    }
 
    private ViewEngineResult OutputViewPath(ViewEngineResult result)
    {
        if(!Debugger.IsAttached || !AtlasData.Settings.Debug.OutputViewNames)
            return result;
 
        if (result.View is RazorView view)
            Debug.WriteLine("ViewPath: " + view.ViewPath);
 
        return result;
    }
}

 

 

Jeff

0

Please sign in to leave a comment.