views:

940

answers:

6

I've just been bitten by a problem where I have a view (FindUser.aspx) trying to render a partial view (FindUser.ascx). The default search paths for views look for a file named after the view in a variety of folders. Rather surprisingly, for views, it looks for a file with the extensions of .aspx or .ascx. And the partial views use the same list.

Because I've got the two files named the same, the view resolution repeatedly finds the page first, and falls into an endless loop.

I know I can fix this either by calling the view and the partial view different names, or by changing my search locations to be .aspx only for views and .ascx only for partial views.

My question is why does MVC default to looking at both extensions? It seems to make more sense that a view == a page == .aspx and a partial view == a control == .ascx. So why muddy the waters?

Cheers Matt

+3  A: 

Because partial or not, a view is still a view. Having FindUser.aspx and FindUser.ascx is the same as having two regular views with the same name.

çağdaş
+1  A: 

For what it's worth I append "Control" to the name of all of my .ascx ViewUserControls. So I would have FindUser.aspx and FindUserControl.ascx. Doesn't solve the problem but it helps you to avoid it by avoiding naming collisions.

tvanfosson
Do not use "Control" term in ASP.NET MVC context. that's just a partial view unless you do partial requests (even then - there are more appropriate names like sub-controllers or widgets). Common convention to mark partial views - underscore prefix in it's name.
Arnis L.
@Arnis - the class is actually ViewUserControl -- I see no problem with using that in the names of my classes or views.
tvanfosson
A: 

You can give MVC the direct path when rendering Views. Say I have a Foo.aspx in my Home folder and a Foo.ascx partial view in Shared. In your action method you can do either:

return View("~/Views/Shared/Foo.ascx"); // or
return View("~/Views/Home/Foo.aspx");

And it will get the proper one you're looking for.

swilliams
+2  A: 

I think that the way to avoid the problem you're having is to use different view names. You probably shouldn't have two views whose file name differs only in extension. However, if you really want a strict Page = View, Control = Partial mapping, just create your own ViewEngine by inheriting from WebFormViewEngine and change the view location formats:

public class MyWebFormViewEngine : WebFormViewEngine {
    public MyWebFormViewEngine() {
        base.ViewLocationFormats 
          = new string[] {"~/Views/{1}/{0}.aspx", "~/Views/Shared/{0}.aspx" };
        base.PartialViewLocationFormats 
          = new string[] { "~/Views/{1}/{0}.ascx", "~/Views/Shared/{0}.ascx" };
    }
}

Then configure it as your View Engine in Application_Start():

// Call this method during Application_Start to setup your view engine
internal static void SetupViewEngines() {
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new MyWebFormViewEngine());
}
anurse
A: 

Reason
View == UserControl in ASP.NET MVC.

Fix
Use different names.

Tip
It`s common convention to name usercontrols with underscore prefix.

Arnis L.
A: 

If you're using Areas, you'll have to add additional LocationFormats in the constructor:

public class ExtensionBasedWebFormViewEngine : WebFormViewEngine
{
  public ExtensionBasedWebFormViewEngine()
  {
     ViewLocationFormats = new[] {"~/Views/{1}/{0}.aspx", "~/Views/Shared/{0}.aspx"};
     AreaViewLocationFormats = new[] {"~/Areas/{2}/Views/{1}/{0}.aspx", "~/Areas/{2}/Views/Shared/{0}.aspx"};

     PartialViewLocationFormats = new[] {"~/Views/{1}/{0}.ascx", "~/Views/Shared/{0}.ascx"};
     AreaPartialViewLocationFormats = new[] { "~/Areas/{2}/Views/{1}/{0}.ascx", "~/Areas/{2}/Views/Shared/{0}.ascx" };
  }
}
Andrew Dunkman