【发布时间】:2017-07-05 12:19:46
【问题描述】:
我已经读到,由于关注点分离,存储库层不应该处理 ViewModel,而应该只处理模型。服务层也是如此(在我的情况下,这是我的业务逻辑所在)。那么控制器就剩下来处理 ViewModel 的数量了。
我有一个模特类别:
public class Category
{
public int ID { get; set; }
public int? ParentCategoryID { get; set; }
public virtual ICollection<Product> Products{ get; set; }
public virtual ICollection<CategoryName> CategoryNames{ get; set; }
}
我有一个 ViewModel CategoryListViewModel 在显示所有类别时使用
public class CategoryListViewModel
{
public int ID { get; set; }
public string Name { get; set; }
public string ParentName { get; set; }
}
我的视图采用 IEnumerable<...categorylistviewmodel>
这就是我从控制器中填充 ViewModel 的方式:
public ActionResult Index()
{
IEnumerable<CategoryListViewModel> model;
List<CategoryListViewModel> list = new List<CategoryListViewModel>();
IEnumerable<Category> categoryList = categoryService.GetAllCategoriesList(RouteData);
foreach (var item in categoryList)
{
CategoryListViewModel temp = new CategoryListViewModel()
{
ID = item.ID,
Name = categoryService.GetCategoryName(RouteData, item.ID)
};
if (item.ParentCategoryID != null)
{
temp.ParentName = categoryService.GetCategoryName(RouteData, (int)item.ParentCategoryID);
}
list.Add(temp);
}
model = list;
return View(model);
}
我的服务方式:
public IEnumerable<Category> GetAllCategoriesList(RouteData data)
{
LanguageService languageService = new LanguageService();
Languages langEnum = languageService.LanguageStringToEnum(languageService.DetermineSelectedLanguage(data));
IEnumerable<Category> allCategories = repository.getAllCategoriesTest();
return allCategories;
}
public string GetCategoryName(RouteData data, int categoryId)
{
LanguageService languageService = new LanguageService();
Languages langEnum = languageService.LanguageStringToEnum(languageService.DetermineSelectedLanguage(data));
return repository.GetCategoryName(langEnum, categoryId);
}
最后是我的存储库方法:
public IEnumerable<Category> getAllCategoriesTest()
{
return db.Category.ToList();
}
public string GetCategoryName(Languages lang, int categoryId)
{
return db.CategoryName.Where(cn => cn.CategoryID == categoryId && cn.Language == lang).Select(cn => cn.Name).FirstOrDefault();
}
这种方法对我来说看起来很糟糕。我的控制器不再瘦了,我正在运行很多查询来寻找这么简单的东西。
如果我在我的存储库中允许 ViewModels,我会得到一个更简洁的解决方案。
我的控制器方法:
public ActionResult Index()
{
return View(categoryService.GetAllCategories(RouteData));
}
服务方式:
public IEnumerable<CategoryListViewModel> GetAllCategories(RouteData data)
{
LanguageService languageService = new LanguageService();
Languages langEnum = languageService.LanguageStringToEnum(languageService.DetermineSelectedLanguage(data));
return repository.SelectAllCategories(langEnum);
}
和存储库方法:
public IEnumerable<CategoryListViewModel> SelectAllCategories(Languages lang)
{
var categories = db.Category.Include(c => c.CategoryNames).Select(names => new CategoryListViewModel
{
ID = names.ID,
Name = names.CategoryNames.Where(cn => cn.Language == lang).Select(cn => cn.Name).FirstOrDefault(),
ParentName = db.CategoryName.Where(cn => cn.Language == lang && cn.CategoryID == names.ParentCategoryID)
.Select(cn => cn.Name).FirstOrDefault()
}).ToList();
return categories;
}
这种方法虽然违反了关注点分离,但对我来说似乎更“干净”。
我的问题是,另一种方法在查询方面不是更有效吗?还有没有其他方法可以做到这一点,以免编写繁重的控制器方法并且不执行那么多查询?在我看来,我错过了什么。
【问题讨论】:
-
我个人使用factory pattern。所以每个视图模型都有一个创建它的视图模型工厂。这将这个创建逻辑与控制器逻辑分开,控制器应该只是一个路由组件。
-
当然要记住的另一件事是模型是域级别的对象。它不需要与表格一一对应。
-
感谢您的快速回复。这个想法会将视图模型的创建移到控制器之外,这是真的。尽管如此,如果我只是移动它,我仍然会为一些简单的事情得到“很多”代码,并且仍然会执行看起来不必要的许多查询。我想知道是否有更好的方法来做到这一点。
-
Entity Framework 肯定有缺陷,但也有很多优点。对于大多数事情,EF 就可以了。如果不是,您可以直接通过 SQL 使用存储过程、用户定义函数等。您甚至可以轻松地将所有这些重新整合到您的上下文中,因此非常无缝。
-
取决于你如何尝试使用它,我猜。实体只是表示关系数据库表的对象。如果您意识到这一点并接受它,那么 EF 的工作方式就非常合理。问题是 ASP.NET MVC 试图将实体作为应用程序的模型,是的,当你这样做时,整个事情都会崩溃。
标签: c# .net asp.net-mvc design-patterns asp.net-mvc-viewmodel