MVC 3 + EF 4.1 + POCO + ViewModel 模式 +带脚手架的控制器==混乱!
经过多年的 ASP.NET 表单开发,我[终于!] 开始处理 MVC(版本 3)。我在 n 层应用程序架构方面拥有深厚的背景,并且我正在尝试正确地处理这个新项目,明确关注点分离等。
我所做的是通过创建 POCO 从代码优先开始。由此,框架创建了我的数据库。
然后,我通过将所有 EF 查询和 CRUD 方法放入模型程序集中每个 POCO 类的存储库类中来实现存储库模式。这样,我的控制器不需要知道我如何通过 EF 访问我的数据。伟大的。
最后,我在模型程序集中创建了一些 ViewModel 类。我的意图是,对于某些操作(例如创建和编辑),我从 RAZOR 视图引用我的 ViewModel 类,而不是我的 POCO 类。这样,我可以拥有 POCO 类以及 SelectList,用于在 ViewModel 中填充下拉列表。两者都通过对关联存储库类的引用来填充,这些类是从我的控制器操作中调用的。我想我现在进展顺利:
class MyObject
{
public int ID {get;set}
[Required]
[StringLength(512)]
public string Name {get;set;}
}
class MyViewModel // ViewModel for a specific view
{
public MyObject MyModel {get;set;} // the model that is being edited
// other data the view might need, set by the controller
public string SomeMessage { get; set; }
public List<SomeObject> SomeObjects {get;set;} /// e.g. for a drop-down list
// My constructor below that populates the "SomeObjects" list, and accepts the
// "MyObject" class as a parameter in order to set the "MyModel" property above...
// ..........
}
问题...
在开始使用控制器创建和编辑操作中的 ViewModel 类之前,我直接传入 POCO 类。当我从视图中的“编辑”表单中单击“保存”按钮时,一切正常:
旧代码:
[HttpPost]
public ActionResult Edit(MyObject mine)
{
if (ModelState.IsValid)
{
myRepository.Update(mine);
myRepository.SaveChanges();
return RedirectToAction("Index");
}
return View(mine);
}
当我单击“保存”时,将返回我的 POCO 类 (MyObject),并自动填充表单中的值,它会成功拯救,生活会很美好。
当我切换到传递我的 ViewModel (MyViewModel) 时,一切都崩溃了。
我可以通过在编辑视图顶部设置 @model 引用来引用我的 ViewModel (MyViewModel)。然后,我可以从属于 ViewModel 的 POCO 类 (MyObject) 填充表单字段。我什至能够从 ViewModel 中的 SomeObjects 集合填充 DropDownList,并从我正在编辑的 MyObject 类中预先选择正确的列表。一切看起来都很好直到...
当我点击保存按钮并调用我的控制器的编辑ActionResult(POST操作)时,传递到ActionResult的MyObject类(public ActionResult Edit(MyObject mine) ) 为空。
然后,我尝试将传入的对象更改为我的 ViewModel (public ActionResult Edit(MyViewModel myVM)),其中引用的 MyObject 类 (MyModel) 为 null。
我错过了什么?
我知道它一定是非常简单的东西,以至于它就在我面前,而我却看不到它!
I'm [finally!] tackling MVC (version 3) after years of ASP.NET forms development. I have a strong background in n-tier application architecture, and I'm trying to approach this new project properly, with a clear separation of concerns, etc.
What I've done is start out with code-first by creating my POCOs. From this, the framework created my database.
Then, I implemented the Repository pattern by putting all my EF query and CRUD methods in a Repository class for each of my POCO classes in my Models assembly. This way, my Controllers don't need to know a thing about how I access my data via the EF. Great.
Finally, I created a few ViewModel classes in my Models assembly. My intent is, for certain actions (such as create and edit), I reference my ViewModel classes from the RAZOR views, instead of my POCO classes. This way, I can have my POCO class as well as a SelectList for populating a Drop Down within my ViewModel. Both populated by references to the associated Repository classes, which are called from my Controller Actions. I think I'm on a roll now:
class MyObject
{
public int ID {get;set}
[Required]
[StringLength(512)]
public string Name {get;set;}
}
class MyViewModel // ViewModel for a specific view
{
public MyObject MyModel {get;set;} // the model that is being edited
// other data the view might need, set by the controller
public string SomeMessage { get; set; }
public List<SomeObject> SomeObjects {get;set;} /// e.g. for a drop-down list
// My constructor below that populates the "SomeObjects" list, and accepts the
// "MyObject" class as a parameter in order to set the "MyModel" property above...
// ..........
}
The Problem...
Before I started using my ViewModel classes from my Controller Create and Edit actions, I passed in the POCO class directly. Everything worked fine when I hit the save button from my Edit form within my view:
Old Code:
[HttpPost]
public ActionResult Edit(MyObject mine)
{
if (ModelState.IsValid)
{
myRepository.Update(mine);
myRepository.SaveChanges();
return RedirectToAction("Index");
}
return View(mine);
}
When I hit save, my POCO class (MyObject) would be returned, automagically populated with values from the form, it would successfully save, and life was peachy.
When I switched to passing in my ViewModel (MyViewModel), everything fell apart.
I was able to refer to my ViewModel (MyViewModel) by setting the @model reference at the top of my Edit view. I was then able to populate my form fields from my POCO class (MyObject) that is part of the ViewModel. I was even able to populate the DropDownList from the SomeObjects collection in the ViewModel and preselect the correct one from my MyObject class I was editing. Everything seemed fine UNTIL...
When I hit the save button and my Controller's Edit ActionResult (POST action) was called, the MyObject class that is passed in to the ActionResult (public ActionResult Edit(MyObject mine)) was null.
Then, I tried changing the passed in object to my ViewModel (public ActionResult Edit(MyViewModel myVM)), which had the referenced MyObject class (MyModel) as null.
What am I missing?
I know it has to be something so incredibly simple that it's staring me in the face and I cannot see it!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
查看FormCollection,键的名称应该与您要填充的类的属性相匹配。这就是 MVC 默认模型绑定的工作原理。
Look at the FormCollection, the names of the keys should match the properties of the class you want to fill. This is how the default modelbinding of MVC works.
维姆,
非常感谢你的帮助。我确实有无参数构造函数,我只是从示例中省略了它。
我实际上找到了这个问题。平心而论,我输入的代码不是实际的代码,因为当我发布此内容时,我面前并没有它。问题是我的 ViewModel 中的实体模型类引用实际上将其设置访问器设置为私有:
这阻止了模型绑定器在控制器的保存方法期间回发时填充该属性。
我现在要做的是将验证逻辑从 EF POCO 移动到 ViewModel,这似乎是此类模式中的推荐操作。
感谢您抽出宝贵的时间,我希望这可以帮助其他刚接触此框架、遇到类似问题的人。
Wim,
Thanks so much for your help. I did have s parameter-less constructor, I had just omitted it from the example.
I actually tracked down the issue. In all fairness, the code I typed in was not the actual code since I didn't have it in front of me when I posted this. What the issue was is that my entity model class reference in my ViewModel actually had its set accessor as private:
This had prevented the modelbinder from populating that property when posting back during the controller's save method.
What I'm left to do now is to move my validation logic from my EF POCO to my ViewModel, as seems to be the recommended action in this type of pattern.
Thanks for your time, and I hope this helps out other people with similar issues who are new to this framework.