【问题标题】:ViewModels in Repository存储库中的 ViewModel
【发布时间】: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


【解决方案1】:

首先,请记住,尽管名称中有“MVC”,但 ASP.NET MVC 只是非常松散地实现了 MVC 模式。 MVC 告诉您有瘦控制器,因为模型是一个活动记录,它处理所有业务逻辑,包括查询本身。这不适用于 ASP.NET MVC。在那里,您的模型实际上是您的 DAL、服务层、实体和一个或多个视图模型的组合。这意味着控制器不可避免地必须至少比 Ruby on Rails 之类的控制器做更多的工作,即使只是将所有这些东西连接在一起。

正如@Liam 在上面的 cmets 中所建议的,您最好的选择是工厂。这样,控制器实际上并不拥有如何将实体映射到视图模型的逻辑。当然,您仍然需要实际调用控制器中的工厂,但逻辑仍然是抽象的。

此外,适当的服务层应该汇总原本在控制器中的逻辑。如果您需要类别的本地化名称,您的服务应该有一个方法可以返回所有类别及其本地化名称。如果您必须多次访问您的服务,这清楚地表明您没有为您的应用程序提供必要的端点。您可能需要引入 DTO 来处理这些数据,因为实体类可能没有适当的属性。然后,您将拥有一个将您的 DTO 映射到视图模型的工厂。

最后,就其价值而言,您的存储库是完全没有必要的。只需让您的服务直接与您的实体框架上下文交互。拥有一个存储库给您带来的只是您必须维护的额外东西。

【讨论】:

  • 感谢您的回复。所以我只是使用 DTO 而不是我的视图模型(在我提到的第二种方法中)?然后我会有一个匿名变量通过服务获取 DTO,然后将 DTO 映射到我的视图模型?我很喜欢这个主意。唯一看起来有点奇怪的是,在这种情况下,我的 DTO 和我的视图模型将是相同的(看看我提出的第二种方法)。这不是不必要的代码重复吗?关于存储库,我同意。我只是想将业务逻辑与数据访问分开。
  • 是和不是。从表面上看,它看起来像一个重复的类,但每个类都是完全隔离的。视图模型关注视图的需求,并且可能会基于此而随着时间的推移而出现分歧。同样,DTO 与服务有关,如果该服务端点需要返回额外的数据,它可能会随着时间的推移而分道扬镳。他们在这个时间点看起来相似的事实是无关紧要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多