我想知道在表单上包含选择列表的最佳方法是什么,该表单包含来自数据库的值而不复制任何代码。
我认为有意义的是将这些数据加载到控制器中并将其传递给视图模型,因此我可以使用SelectListFor<>
或其他任何内容来生成列表。但是这意味着我必须在GET和POST方法中复制所有列表加载。我能看到的另一种方法是将数据库上下文传递给视图模型构造函数并让它加载列表,但这会产生另外两个问题:
1)视图模型是否应该了解数据库上下文?
2)然后,我不能通过接受视图模型类型作为方法参数来使用模型绑定,因为它没有无参数构造函数(如果我创建一个无参数构造函数,那么如果我没有列表,它将不具有列表想要重新显示包含表单的视图。
有更好的方法吗?这似乎是一个相当普遍的场景,任何建议都会受到赞赏。
答案 0 :(得分:1)
为什么不给你db或存储库或业务规则 - 无论你怎么称呼它都会发回一个IDictionary ???
此示例假设您有一个用户列表,您将发回一个包含其ID和值的密钥,让我们说出名字+姓氏:
然后在视图中使用它....
<%= Html.DropDownListFor(model => model.UserID, new SelectList(Model.AvailableUsers, "Key", "Value",Model.UserID))%>
model.UserID = Key
Model.AvailableUsers = IDictionary<int,string>
我在一些帮助程序代码中创建我的列表,然后我使用这个帮助程序查找这些值...所以有一个集中式类(通常是静态的)将生成这些“用户”......
将这些用户直接传递到视图上,或者像您的情况一样将ViewModel传递给我 - 这是我推荐的
注意:您不会使用List / Model Binding连接数据上下文,这会使事情过于复杂。只需从列表中选择UserID作为所选用户,然后在你的帖子句柄中使用...
视图模型:
public class UsersViewModel
{
public int UserID { get; set; }
public IDictionary<int,string> AvailableUsers{ get; set; }
}
在你的帖子中......
[HttpPost]
[ValidateAntiForgeryToken]
[DemandAuthorization(RoleNames.AdminUser, AlwaysAllowLocalRequests = true)]
public ActionResult AllUsers(int UserID)
{
try
{
//save to db or whatever...
return RedirectToAction("Index", "Users");
}
catch (RulesException ex)
{
ex.CopyTo(ModelState); //custom validation copied to model state
}
var _users= _service.GetUsers();
return View("AllUsers", new UsersViewModel
{
UserID = UserID,
AvailableUsers = _users
});
}
答案 1 :(得分:1)
我们通常通过ReferenceDataRepository实现我们的查找,这些查找在控制器中以与任何其他存储库交互相同的方式使用。这个存储库通常会收到大量主要是静态只读数据的调用,因此我们可以使用您选择的缓存方案(Session,AppFabric等)的抽象来实现派生的CachedReferenceDataRepository。
答案 2 :(得分:0)
嗯,在我看来,你必须在viewmodel中有上下文或存储库对象才能在这种情况下保持干燥。此外,您的viewmodel不应该知道您的数据库也是正确的。要处理此问题,您可以让viewmodel的构造函数接受类似
的接口public Interface IUserRepository
{
public IEnumerable<User> GetAll();
}
您可以将视图模型设为
public class CreateVM
{
private IUserRepository _repo;
public CreateVM(IUserRepository repo)
{
_repo = repo;
}
public IEnumerable<SelectListItem> AvailableUsers
{
get{
return _repo.GetAll().Where(x=>x.isAvailable).Select(x=>new SelectListinItem{Text = x.UserName, Value = x.UserID});
}
}
}
最后一块拼图是你的DI设置。告诉你IOC容器只要实例化它就在viewmodel的构造函数中注入IUserRepository。我不知道当模型绑定器创建一个视图模型的实例时DI是否可以工作,但它至少在理论上是存在的。你的viewmodel不知道你的存储库,但只知道一个接口,你的列表是在单点创建的,所以你也是干的。
答案 3 :(得分:0)
我将IRepository
传递给ViewModel时遇到的最大问题是,这很容易导致性能问题 - 在这种情况下,选择n + 1的非常自然,很难避免他们。您希望尽可能少地为DB提供rountrips,并且让IRepository
传递所有这些多级ViewModel并没有帮助。
相反,您可以引入ViewModel工厂,该工厂负责创建所需类型的ViewModel。 MyViewModelFactory
取决于IRepository
,并会创建MyViewModel
,这只是简单的DTO。