mercredi 5 juillet 2017

ViewModels in Repository

I have read that the repository layer should not deal with ViewModels because of separation of concerns and should instead deal only with Models. This is also true for the service layer (in my case this is where my business logic is). So then the controller is left to deal with the population of the ViewModels.

I have a Model Category:

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; }
}

I have a ViewModel CategoryListViewModel used when displaying all Categories

public class CategoryListViewModel
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string ParentName { get; set; }
    }

My view takes IEnumerable<...CategoryListViewModel>

This is how I populate the ViewModel from the controller:

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);
        }

My service methods:

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);
        }

And finally my repository methods:

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();
        }

This approach looks very bad to me. My Controller is not thin anymore and I am running a lot of queries for something that simple.

If I allow ViewModels in my repository I get a much cleaner solution.

My Controller method:

public ActionResult Index()
        {
            return View(categoryService.GetAllCategories(RouteData));            
        }

Service method:

public IEnumerable<CategoryListViewModel> GetAllCategories(RouteData data)
        {
            LanguageService languageService = new LanguageService();
            Languages langEnum = languageService.LanguageStringToEnum(languageService.DetermineSelectedLanguage(data));

            return repository.SelectAllCategories(langEnum);
        }

And repository method:

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;                             
        }

This approach, although violating separation of concerns seems to be "cleaner" to me.

My question is isn't the other approach more efficient in term of queries? Also is there any other way that this could be done so as not to write heavy controller methods and not execute that many queries? It seems to me that I am missing something.

Aucun commentaire:

Enregistrer un commentaire