asp.net mvc - 更新/传递对象,控制器和视图之间没有关联
本质上我拥有的是一个实体类(文章),一篇文章可能属于n类别。这是人们期望类别字段(例如 ICollection)的地方,但数据库不是以这种方式构建的,我无法修改实体框架模型。
当在视图中选择一个类别时,用户必须选择一个属性(1 到 10 之间的字符串列表。它看起来像这样:
Category Option
===============================
[x] Category 1 |---------|v|
[x] Category 2 |---------|v|
[x] Category 3 |---------|v|
[x] Category 4 |---------|v|
[x] - checkbox
|---|v| - dropdown list
但是我在想也许我可以为这种特殊情况创建一个 ViewModel。我是 视图
namespace Models.ViewModels
{
public class ArticleViewModel
{
public Article Article { get; set; }
public ICollection<Category> Categories { get; set; }
public ArticleViewModel()
{
Categories = new List<Category>();
}
}
}
但是,当我将表单发布到控制器时,ArticleViewModel 及其所有内部都是空的,我不确定为什么
本身非常基本,它将有两种单独的表单:一种用于修改文章详细信息,另一种用于修改文章详细信息。 作为一个刚接触 ASP.NET MVC 的人,我
觉得解决这个问题有点困难,
会推荐哪种方法?
您 路由看起来
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Models.ViewModels.ArticleViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Modify article: <%= Model.Article.Title %>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm()) {%>
<%= Html.ValidationSummary(true) %>
<fieldset>
<legend>Properties</legend>
<div class="property">
<span>
<%= Html.LabelFor(model => Model.Article.Title) %>
<%= Html.TextBoxFor(model => Model.Article.Title) %>
<%= Html.ValidationMessageFor(model => Model.Article.Title) %>
</span>
</div>
<div class="property">
<span>
<%= Html.LabelFor(model => Model.Article.Type) %>
<%= Html.TextBoxFor(model => Model.Article.Type) %>
<%= Html.ValidationMessageFor(model => Model.Article.Type) %>
</span>
</div>
[continued...]
<div>
<%= Html.HiddenFor(model => Model.Article.Id) %>
<input type="submit" value="Save" />
</div>
</fieldset>
<% } %>
</asp:Content>
像这样:
routes.MapRoute(
"Articles",
"Articles/{action}/{id}",
new { controller = "Articles", action = "Edit" }
);
Essentially what I have is an entity class (Article) and one article may belong to n categories. This is where one would expect a field for the categories (say an ICollection) but the database is not built that way and I cannot modify the Entity Framework model.
When a category is selected in the view, the user has to select a property (a string list between 1 to 10. It will look like this:
Category Option
===============================
[x] Category 1 |---------|v|
[x] Category 2 |---------|v|
[x] Category 3 |---------|v|
[x] Category 4 |---------|v|
[x] - checkbox
|---|v| - dropdown list
However I'm thinking that perhaps I could create a ViewModel for this particular case. I was thinking something like
namespace Models.ViewModels
{
public class ArticleViewModel
{
public Article Article { get; set; }
public ICollection<Category> Categories { get; set; }
public ArticleViewModel()
{
Categories = new List<Category>();
}
}
}
However when I post the form to the controller the ArticleViewModel and all of its internals are null. I'm not sure why.
The view itself is pretty basic, it will have two separate forms: one for modifying article details and one for assigning categories to the article.
As a guy new to asp.net mvc, I find it a little difficult to wrap my head around this problem.
Which approach would you recommend?
Update: added code for the view.
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Models.ViewModels.ArticleViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Modify article: <%= Model.Article.Title %>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm()) {%>
<%= Html.ValidationSummary(true) %>
<fieldset>
<legend>Properties</legend>
<div class="property">
<span>
<%= Html.LabelFor(model => Model.Article.Title) %>
<%= Html.TextBoxFor(model => Model.Article.Title) %>
<%= Html.ValidationMessageFor(model => Model.Article.Title) %>
</span>
</div>
<div class="property">
<span>
<%= Html.LabelFor(model => Model.Article.Type) %>
<%= Html.TextBoxFor(model => Model.Article.Type) %>
<%= Html.ValidationMessageFor(model => Model.Article.Type) %>
</span>
</div>
[continued...]
<div>
<%= Html.HiddenFor(model => Model.Article.Id) %>
<input type="submit" value="Save" />
</div>
</fieldset>
<% } %>
</asp:Content>
Routing looks like this:
routes.MapRoute(
"Articles",
"Articles/{action}/{id}",
new { controller = "Articles", action = "Edit" }
);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
MVC 的重点是视图与模型分离。您的 EF 实体是什么样子并不重要。
按照您想要的方式创建视图和视图模型类。不要考虑您的数据库结构。如果您需要类别集合,请创建它。此代码应代表 UI 的“域”。
在控制器中将视图模型映射到 EF,反之亦然。控制器有责任理解数据访问代码(或服务层代码)和视图代码。
这种方法允许您充分利用 MVC 以及 UI 和数据库之间的解耦。尽管您最终为模型编写了更多代码,但稍后会带来利息(您可以修改数据库而无需触摸 UI,反之亦然)。
如果您发现自己花费大量时间将数据从 EF 实体复制到模型,那么您可能需要看看这个出色的库:自动映射器
The whole point of MVC is that your View is decoupled from your Model. It doesn't matter what your EF entities look like.
Create your view and view model classes the way you want. Don't think about your DB structure. If you need a collection of Categories than create it. This code should represent the 'domain' of UI.
In the controller map your view model to EF and vice versa. It's controllers responsibility to understand data access code (or service layer code) and view code.
This approach allows you to take a full advantage of MVC and decoupling between UI and database. Although you end up writing a bit more code for your models it pays up with interest later on (you can modify your db without touching UI and vice versa).
If you find yourself spending a lot of time copying data from EF entities to models than you might want to take a look at this brilliant library: AutoMapper
当您像这样设置视图时,每个字段都将具有相同的
name
属性,并且我认为 MVC 不知道如何将其正确映射回单个项目。您的[HttpPost]
操作是什么样的?如果它是这样的:那么我怀疑它会正确映射。您也许可以尝试接受
List
,但我认为如果没有CustomModelBinder
,这将无法正常工作。@Jakub 说的是正确的:解耦你的思维并以最符合视图逻辑的方式设计你的 ViewModel;您可以稍后再将其映射回服务层。这无疑是一种范式转变,但值得做出。 =)
When you set up your view like that, each of those fields is going to have the same
name
attribute and I don't think MVC will know how to map it back to a single item properly. What does your[HttpPost]
action look like? If its anything like:then I doubt it will map correctly. You could maybe try accepting
List<ArticleViewModel>
but I don't think that would work correctly without aCustomModelBinder
.What @Jakub said is correct: de-couple your thinking and design your ViewModel in the most logical way for the view; you can worry about mapping it back to the service layer later. Its a paradigm shift for sure, but one that's worth making. = )