带有相对路径的 ASP.Net MVC 和 RenderPartial
我一直在玩 ASP.NET MVC 并有一个问题。 或者也许是担心我做错了。 只是在一个蹩脚的网站上工作来伸展一下我的翅膀。 很抱歉这个问题一点也不简洁。
好的,这是场景。 当用户访问 home/index 时,页面应显示产品列表和文章列表。 文件布局是这样的(DAL 是我的数据访问层):
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
但是这种方法失败了。 这种方法对我来说很有意义,但也许对这些 MVC 平台有更多经验的人会认识到更好的方法。
上面的代码在 View.Product.List 中产生错误。 对 Html.RenderPartial("Single",...)
的调用抱怨找不到“Single”视图。 该错误表示:
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
因为我从 Product 中的视图调用 RenderAction(),所以我希望运行时在 Views\Product 中查找“单个”视图。 然而,查找似乎是相对于调用原始视图(/Controller/Home 调用/Views/Product)的控制器而不是当前视图。
因此,我可以通过更改 Views\Product 来修复此问题,这样:
View.Product.List will call
foreach(Product product in View.Model)
Html.RenderPartial("~/Views/Product/Single.ascx", product);
而不是
View.Product.List will call
foreach(Product product in View.Model)
Html.RenderPartial("Single", product);
此修复有效,但是.. 我不明白为什么我需要指定视图的完整路径。 对我来说,相对于当前视图的路径而不是原始控制器的视图路径来解释相对名称是有意义的。 我想不出任何有用的情况,解释相对于控制器视图而不是当前视图的名称是有用的(除了它们相同的典型情况)。
这个时候我应该会产生一个问号吧? 强调这一点其实是一个问题。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我认为您误解的部分是“执行位置”,因为缺乏更好的或官方的术语。 路径与您的视图无关,甚至与您所说的“控制器的视图”无关。 它们与您的请求 URL 相关,该 URL 定义了控制器上下文。 我可能说得不太清楚,但如果您在 Reflector 中花一点时间研究一下 URL 和路由是如何解析的,我想这一切都会在您的脑海中浮现。
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.
[编辑:
我在想,你有两种情况:
在第一种情况下,视图用户控件确实属于主页控制器,将它们放在主控制器文件夹中是有意义的。 在第二种情况下,将它们放在共享文件夹中是有意义的,因为它们将由控制器共享。
无论哪种情况,也许您都可以将它们放在子文件夹中。 像 Views/Home/Products 一样,然后尝试 RendarPartial("Product/Single") 看看会发生什么? 我不知道它是否会尝试将其解析为:Home/Product/Single,然后是 Shared/Product/Single。 如果子文件夹有效,它似乎允许产品和文章的逻辑分离,同时表明它们仍然是主控制器的成员或由所有控制器共享。
]
查看 Steve Sanderson 的博客文章:
http ://blog.codeville.net/2008/10/14/partial-requests-in-aspnet-mvc/
你所做的并没有错,但它似乎有点违反了惯例查看/控制器文件夹名称。 也就是说,想要定义与控制器无关的视图用户控件是有意义的,并且嵌套它们似乎是有效的。 所以我不知道!
无论如何,该链接只是描述了一种方法,而不是使用 RenderPartial 来呈现使用控件,它定义了 RenderPartialRequest 的方法,该方法呈现控制器操作的返回值(在您的情况下是用户控件)。 因此,您可以添加一个具有返回用户控件的操作列表的产品和文章控制器,然后从主页/索引视图调用这两个操作。 这对我来说似乎更直观,但只是一个意见。
他还提到了 MVC Contrib 的子控制器,我非常确定人们希望这样的东西成为 ASP.NET MVC 版本的一部分。
[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.
从 MVCStoreFront 示例来看,这就是他们如何构建调用 RenderPartial 的所有内容,
然后通过以下方式渲染它们:
From looking at the MVCStoreFront sample this is how they have everything structured for calling RenderPartial
Then render them via: