I've been playing around with ASP.NET MVC and had a question. Or maybe its a concern that I am doing this wrong. Just working on a lame site to stretch my wings a bit. I am sorry this question is not at all concise.
Ok, here's the scenario. When the user visits home/index, the page should show a list of products and a list of articles. The file layout is such (DAL is my data access layer):
Controllers Home Index Views Home Index inherits from ViewPage Product List inherits from ViewUserControl<IEnumerable<DAL.Product>> Single inherits from ViewUserControl<DAL.Product> Article List inherits from ViewUserControl<IEnumerable<DAL.Article>> Single inherits from ViewUserControl<DAL.Article>
Controllers.HomeController.Index produces a View whose ViewData contains two entries, a IEnumerable<DAL.Product> and a IEnumerable<DAL.Article>.
View.Home.Index will use those view entries to call:
Html.RenderPartial("~/Views/Product/List.ascx", ViewData["ProductList"])
and Html.RenderPartial("~/Views/Article/List.ascx", ViewData["ArticleList"])
View.Product.List will call
foreach(Product product in View.Model)
Html.RenderPartial("Single", product);
View.Article.List does something similar to View.Product.List
This approach fails however. The approach makes sense to me, but maybe someone with more experience with these MVC platforms will recognize a better way.
The above produces an error inside View.Product.List. The call to Html.RenderPartial("Single",...)
complains that "Single" view was not found. The error indicates:
The partial view 'Single' could not be found. The following locations were searched: ~/Views/Home/Single.aspx ~/Views/Home/Single.ascx ~/Views/Shared/Single.aspx ~/Views/Shared/Single.ascx
Because I was calling RenderAction() from a view in Product, I expected the runtime to look for the "Single" view within Views\Product. It seems however the lookup is relative the controller which invoked the original view (/Controller/Home invoked /Views/Product) rather than the current view.
So I am able to fix this by changing Views\Product, such that:
View.Product.List will call
foreach(Product product in View.Model)
Html.RenderPartial("~/Views/Product/Single.ascx", product);
instead of
View.Product.List will call
foreach(Product product in View.Model)
Html.RenderPartial("Single", product);
This fix works but.. I do not understand why I needed to specify the full path of the view. It would make sense to me for the relative name to be interpreted relative to the current view's path rather than the original controller's view path. I cannot think of any useful case where interpreting the name relative to the controller's view instead of the current view is useful (except in the typical case where they are the same).
Around this time I should have a question mark? To emphasis this actually is a question.
Because I was calling RenderAction() from a view in Product
...
I do not understand why I needed to specify the full path of the view. It would make sense to me for the relative name to be interpreted relative to the current view's path rather than the original controller's view path
The part I think you're misunderstanding is the "execution location" for lack of a better or official term. Paths are not relative to your view, not even your "controller's view" as you put it. They are relative to your request URL, which defines a controller context. I may not be saying it very well, but if you spent a little time in Reflector looking at how URLs and routes are resolved, I think this would all fall into place in your head.
[edit:
I was thinking, you have 2 cases:
In the first case, the view user controls really belong to the home controller and it makes sense to put them in the home controller folder. In the second case, it makes sense to place them in the shared folder since they will be shared by controllers.
In either case, maybe you can place them in a sub folder. Like Views/Home/Products and then try RendarPartial("Product/Single") and see what happens? I don't know if it would try to resolve it to: Home/Product/Single and then Shared/Product/Single or not. If sub folders work, it seems to allow the logical seperation of Product and Article, while showing that they are still members of either the Home controller or Shared by all controllers.
]
Check out this blog entry by Steve Sanderson:
http://blog.codeville.net/2008/10/14/partial-requests-in-aspnet-mvc/
What you are doing isn't wrong, but it does seem to sort of go against the convention of View/Controller folder names. That said, it makes sense to want to define controller-agnostic view user controls and nesting them seems valid. So I dunno!
Anyways, the link just describes a method of instead of using RenderPartial to render a use control, it defines a method of RenderPartialRequest that renders the return value (in your case a user control) of a controller action. So you could add a Product and Articles controller with an Action List that returns your user control, and then call those two actions from the Home/Index view. This seems more intuitive to me, but just an opinion.
He also mentions subcontrollers from MVC Contrib, and I'm pretty sure there is desire for something like this to be a part of ASP.NET MVC release.
From looking at the MVCStoreFront sample this is how they have everything structured for calling RenderPartial
Views
Shared
ProductSingle
ProductList
ArticleSingle
ArticleList
Then render them via:
<% Html.RenderPartial("ProductSingle", ViewData["ProductList"]); %>
<% Html.RenderPartial("ProductList", product); %>
<% Html.RenderPartial("ArticleSingle", article); %>
<% Html.RenderPartial("ArticleList", ViewData["ArticleList"]); %>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With