dimanche 21 juin 2020

When and how to initialize (View)models using constructors with the aim to set all props of a object correctly and forget none

So I have this simple Edit action method which builds a viewmodel

public async Task<IActionResult> Edit(int id)
{
    var product = await _productRepo.Get(id);
    if (product == null) return RedirectToAction(nameof(Index));
    var categories = await _categoryRepo.Get();
    var suplliers = await _supplierRepo.Get(); ;
    var editVM = new EditVM()
    {
        Product = product,
        Categories = categories,
        Suppliers = suplliers
    };
    return View(editVM);
}

And for completeness here is the EditVM

public class EditVM
{
    public Product Product { get; set; }
    public IEnumerable<Category> Categories { get; set; }
    public IEnumerable<Supplier> Suppliers { get; set; }
}

I obviously use it to edit a product. So far so good.

Now I wanted to reuse this EditVM and my Edit.cshtml view for creating products so I created the Create action method

public async Task<IActionResult> Create()
{
    var categories = await _categoryRepo.Get();
    var editVM = new EditVM()
    {
        Product = Product(),
        Categories = categories
        //Notice I forget to initialize Suppliers here!!
    };
    return View("Edit", editVM);
}

I got this null error because I forgot to initialize the Suppliers prop in my Create action method. So I improved it by changing my EditVM to only be initialized with a constructor with the required params and set the props to private setters:

public class EditVM
{
    public EditVM(Product product, IEnumerable<Category> categories, IEnumerable<Supplier> suppliers)
    {
        Product = product;
        Categories = categories;
        Suppliers = suplliers;
    }

    public Product Product { get; private set; }
    public IEnumerable<Category> Categories { get; private set; }
    public IEnumerable<Supplier> Suppliers { get; private set; }
}

And now use it like:

public async Task<IActionResult> Edit(int id)
{
    var product = await _productRepo.Get(id);
    if (product == null) return RedirectToAction(nameof(Index));
    var categories = await _categoryRepo.Get();
    var suplliers = await _supplierRepo.Get();
    var editVM = new EditVM(product, categories, suppliers);
    return View(editVM);
}//And the same way in my Create action method

So now I can never forget to set anything and enforced the EditVM is always set with the required props. Is this style recommended? The EditVM is so plain simple...Its my expirience that when viemodels(or 'ajax return models' grow bigger programmers tend to forget some props. We can enforce this by using constructors with private setters or factories. Is this the right way to go? And in this case I just want to have a simple list with all the suplliers using _supplierRepo.Get() in my Create and my Edit action method. But what if I use a very complex query. Then I might want to pass in my repos as params to my EditVM constructor like this:

public EditVM(Domain.Product product, ICategoryRepo categoryRepo, ISupplierRepo supplierRepo)
{
    Product = product;
    Categories = categoryRepo.Get().Result;//My repos are async...
    Suppliers = supplierRepo.Get().Result;//Possible very complex query to be only done at one location
}

Any thoughts about my question are most welcome.

Aucun commentaire:

Enregistrer un commentaire