【发布时间】:2015-10-09 13:26:11
【问题描述】:
假设我有一个复杂的视图模型,其中包含大量数据,例如国家、产品、类别等列表,每次创建 ViewModel 时我都需要从数据库中获取这些数据。
我要解决的主要问题是,当我处理 POST 操作时,一些 TestModel 发布的值不正确,导致 ModelState.IsValid 变为 false,然后我必须返回与当前发布的相同的视图模型。这迫使我再次获取类别列表,因为我是在 GET 操作中这样做的。这在控制器中添加了很多重复的代码,我想将其删除。目前我正在做以下事情:
我的模型和视图模型:
模型,存储在数据库中的实体:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Category> SubCategories { get; set; }
}
查看模型:
public class CategoryModel
{
public int Id { get; set; }
public string Name { get; set; }
}
public class TestModel
{
[Required]
[MaxLength(5)]
public string Text { get; set; }
public int SelectedCategory { get; set; }
public IEnumerable<CategoryModel> Categories { get; set; }
public SelectList CategoriesList
{
get
{
var items = Categories == null || !Categories.Any()
? Enumerable.Empty<SelectListItem>()
: Categories.Select(c => new SelectListItem
{
Value = c.Id.ToString(),
Text = c.Name
});
return new SelectList(items, "Value", "Text");
}
}
}
我的控制器:
public class HomeController : Controller
{
private readonly Repository _repository = ObjectFactory.GetRepositoryInstance();
public ActionResult Index()
{
var model = new TestModel
{
Categories = _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
})
};
return View(model);
}
[HttpPost]
public ActionResult Index(TestModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Succes");
}
model.Categories = _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
});
return View(model);
}
public ActionResult Succes()
{
return View();
}
}
我想删除重复的类别获取和映射,基本上是这段代码:
.Categories = _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
})
来自控制器。我还想删除ModelState 有效性检查,我只想在ModelState.IsValid 时执行操作以保持控制器代码尽可能清洁。到目前为止,我有以下解决方案来删除ModelState 有效性检查:
创建自定义 ValidateModelAttribute
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var viewData = filterContext.Controller.ViewData;
if(viewData.ModelState.IsValid) return;
viewData.Model = filterContext.ActionParameters["model"];
filterContext.Result = new ViewResult
{
ViewData = viewData,
};
}
}
现在模型在操作执行之前得到验证。在验证错误的情况下,我们使用相同的视图和最近发布的相同模型。因此,控制器 POST 操作如下所示:
[HttpPost]
[ValidateModelAttribute]
public ActionResult Index(TestModel model)
{
// Do some important stuff with posted data
return RedirectToAction("Success");
}
这很好,但现在我的TestModel 的Categories 属性为空,因为我必须从数据库中获取类别,并相应地映射它们。 那么可以将我的视图模型修改为如下所示:
public class TestModel
{
private readonly Repository _repository = ObjectFactory.GetRepositoryInstance();
...
public int SelectedCategory { get; set; }
public IEnumerable<CategoryModel> Categories {
get
{
return _repository.Categories.Select(c => new CategoryModel
{
Id = c.Id,
Name = c.Name
});
}
}
...
}
这将使我们拥有非常干净的控制器,但它不会导致某种性能或架构问题吗?它不会破坏视图模型的单一责任原则吗? ViewModel 是否应该负责获取它需要的数据?
【问题讨论】:
-
理想情况下不会,您的视图模型不会直接与您的存储库交互。如果您需要从存储库中填充模型,这将发生在您的控制器中。如果您不想在单独的控制器路由中复制类别填充,您可以尝试将此逻辑重构为单独的方法。
-
很高兴知道我不是唯一做错的人......
标签: c# asp.net-mvc viewmodel