2
\$\begingroup\$

I'm new to MVC and i'm having some difficulty on understanding the proper usage of ViewModels and Controllers.

I have this example where i will list several different informations in my HomeController as CTAs, Banners, News, Courses... and i came up with this ViewModel structure after some research :

public class CoursesViewModel
{
    private readonly Entities _db = new Entities();
    public List<Courses> Courses { get; private set; }

    public Courses ViewModel() { Initialize( "", "", 0 ); }

    public CursosViewModel( string where, string order, int take ) { Initialize( where, order, take ); }

    private void Initialize( string where, string order, int take )
    {
        IQueryable<Curso> query = _db.Curso;

        if( !string.IsNullOrEmpty( where ) )
            query = query.Where( @where );

        if( !string.IsNullOrEmpty( order ) )
            query = query.OrderBy( @order );

        if( take > 0 )
            query = query.Take( @take );

        Courses = query.ToList();
    }
}

public class BaseViewModel
{
    private readonly Entities _db = new Entities();

    public Page Page { get; private set; }
    public List<State> States { get; private set; }
    public List<HtmlBlock> Blocks { get; private set; }

    public Page GetPage( int pageId )
    {
        return Pagina = _db.Page.FirstOrDefault( x => x.ID == pageId ) ?? new Page();
    }

    public void SetBlocks( Page page )
    {
        Page = page;
    }

    public void SetBlocks( List<HtmlBlock> blocks )
    {
        Blocks = blocks.OrderBy( x => x.Ordem ).ToList();
    }

    public void SetStates( List<State> states )
    {
        States = states;
    }
}

And this is my Controller:

public class HomeController : Controller
{
    private readonly Entities _Db = new Entities();

    [MinifyHtml]
    public ActionResult Index()
    {
        var HtmlParser = new HtmlParser();
        var Redirect   = HelperMethods.GetRouteRedirect( HttpContext );
        var BaseVM     = new BaseViewModel();

        var page = BaseVM.GetPage( Redirect.pageId );

        if( page == null ) return new TransferResult( "/not-found" );

        //My ViewModel calls happen inside the HtmlParser.ParseBlocks,
        //there i will get the right ViewModel to each View and instantiate it
        BaseVM.SetBlocks( HtmlParser.ParseBlocks( page.html, ControllerContext ) );
        BaseVM.SetStates( ViewBag.states );

        return View( BaseVM );
    }
}

Is this the correct "usage" for the ViewModel and Controller ? Should i use my Entities (_db) inside a closure ( using ) ? Why ?

Another thing, i have some common information that exists on every page, how can i work with this on ViewModels without repeating the properties on each ViewModel. I created a BaseViewModel, should i extend it on every ViewModel or should i implement it as a property ?

\$\endgroup\$
  • \$\begingroup\$ Properties must be PascalCase. And what's with the odd habit of having a space after ( and before )? \$\endgroup\$ – BCdotWEB Mar 11 '16 at 14:15
  • \$\begingroup\$ Thanks for the PascalCase tip, about the odd habit, for me it's easier to read it and identify what is happening. Is it wrong ? \$\endgroup\$ – Terkhos Mar 11 '16 at 14:18
  • \$\begingroup\$ @BCdotWEB should i also use this for database names ? \$\endgroup\$ – Terkhos Mar 11 '16 at 14:28
  • \$\begingroup\$ You're on right track. One suggestion, move the parrallel invoke into the ViewModel, controller should only new up instance when needed and or call vm methods on post back. Also, the MVC binding engine will new up an instance for you if you put it as an input parameter to the actionmethod. You don't even have to new it up in the action method either. Just make sure to check for modelstate.isvalid on post backs. \$\endgroup\$ – John Peters Mar 12 '16 at 14:07
  • \$\begingroup\$ @JohnPeters i don't really need to post information to my server using the forms, i usually use ajax because i don't want users to have page reload. Would i also need to use this as you said ? If so could you kindly give me an example ? Thank you \$\endgroup\$ – Terkhos Mar 14 '16 at 12:15
2
\$\begingroup\$

In your case CoursesViewModel includes the logic of retrieving entities from db. It should be moved to a service/a repository. ViewModel should include only properties to be shown in the view and some displaying logic in order not to put a lot of server code to the view.

public class HomeController : Controller
{
    // should CRUD products from db 
    private readonly IProductService _productService;

    public HomeController(IProductService productService)
    {
        _productService = productService;
    }

    [HttpGet]
    public ActionResult Index()
    {
        var products = _productService.GetProducts(123);
        var model = new ProductViewModel(products);
        return View(model);
    }
}

public class ProductViewModel
{
    public ProductViewModel(IEnumerable<Product> products)
    {
        // init viewmodel here or use some mapper
        this.products = products;
    }

    // only those properties which are going to be displayed in your view

    public int ProductsCount { get; set; }
    public decimal Total { get; set; }
    public string VatCodeApplied { get; set; }

    // ...

    private IEnumerable<Product> products;
}

public interface IProductService
{
    IEnumerable<Product> GetProducts(int userAccountId);

    // ...
}

public class Product
{
    // ...
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.