The MVC journey begins

Hey folks,

I’ve researched this before asking, but all I can find is trivial apps that all use the same type of example. My first, of many, questions involves Controllers->Views.

Of all of the examples I have seen they always return one view. For example, the Home/Index control will return a view like so: return View(model); But, what if you need multiple views for the same page? Say you have to return two pieces of data, for other parts of the page, would you need to add all control logic in the single Index Controller View(), or can I add different ActionResults for the same page?

Kind of a noob question, but I finally stopped planning, and started building.

Thanks for your help.

Welcome aboard Patri…uh Chaser!

Ok, the View() method has many overloads.

Controller.View Method (System.Web.Mvc)

You can return any kind of view you want at any time during an action.

The same goes for action results.

::smile::

My first Ah ha! Basically you can return all data, for different parts of the page, buy creating a “master” model, such as your “IndexViewModel”? Return it to the view, and pull the data from the controller to the corresponding code to be used in the page, ala Razor, for example.

I hope I am understanding it right.

EDIT: Or a ViewData[“Hello”] = “World”;

for simple string variables.

For multiple piece of data, you’d use a compound viewmodel.

Let’s say I wanted to return a list of menu items, and some articles…

public class MenuItem
{
public string Title { get; set; }
public string Url { get; set; }
}

public class ArticleItem
{
public string Title { get; set; }
public string Message { get; set; }
}

public class IndexViewModel
{
public IEnumerable<MenuItem> MenuItems { get; set; }
public IEnumerable<ArticleItem> ArticleItems { get; set; }
}

Just build and return the IndexViewModel.

Correct, though usually, if you are using a master view then create a MasterViewModel class and put anything in it that is common to all views…

public class MasterViewModel
{
public string DEBUG_MESSAGE { get; set; }
public IEnumerable<MenuItem> MenuItems { get; set; }
public string CopyrightNotice { get; set; }
}

Then base you other top level view models from that…

public class HomeIndexViewModel : MasterViewModel
{
public IEnumerable<ArticleItem> ArticleItems { get; set; }
}

Then create a base controller class and add a special handler…

public abstract MasterController : Controller
{

public ActionResult PreparedView(MasterViewModel model)
{
// set up master view model elements here…
return View(model);
}

And in your top level controller…

public ActionResult Index()
{
var articles = repository.GetAll();
var items = new List<ArticleItem>();
// fill items and set model…
var model = new IndexViewModel { ArticleItems = items };
return PreparedView(model);

}
}

Hope that made sense.

I am also available on MSN Live Chat (serenarules@yadtel.net) at nearly all hours. I have a lot of time on my hands. =)

So, the masterviewmodel is basically a master page from WebForms where certain parts will never change. Then, add page specific features to each page by inheriting it. Followed by, The master control, that inherits from the Controller Object, with an ActionResult which will display the master page items and also the specific Index() ActionResult that calls the PreparedView(model) data?

So, in a sense, the PreparedView() is the last step before it reaches the view (and not the ActionResult Index())?

Your defiantly the go-to person! Thanks for your help

Essentially. Think of it like this:

View uses HomeIndexViewModel
_Layout uses MasterViewModel

The “model” object is passed down from view to _layout and is downcast in the process, so that _layout has access to only the base model properties.

I’ve prepared a little solution for you that might help. Just unzip, load into VS and run. No DB required, though it does have an in-memory db and some repository examples, just to have something to pull from.

Yeah, it’s so much easier to understand when you actually have it in front of you! :wink:

OK, i’m starting to get it, slowly but surely. I’m going to alter your code to see what I come up with, after the foundation you laid.

I will defiantly get back with you tomorrow! Again, thanks for your time. :slight_smile:

You’re welcome Chaser. =)

Morning SerenaRules, I added my question to the code, to make it easer to understand:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using PaperChaser.Models;
using PaperChaser.Models.Domain;
using PaperChaser.Models.Domain.Persistence;
using PaperChaser.Models.Persistence;

namespace PaperChaser.Controllers
{

    [HandleError]
    public class PersonController : MasterController
    {

        private IPersonRepository personRepository;

        public PersonController(IPersonRepository personRepository)
        {
            this.personRepository = personRepository;
        }

        public PersonController()
        {
         
            this.personRepository = new PersonRepository();
        }

        public ActionResult Index()
        {

            var people = personRepository.GetAll();

            var items = new List<PersonIndexViewModelItem>();

            foreach (var person in people)
                items.Add(new PersonIndexViewModelItem
                {
                    Id = person.Id,
                    Name = person.Name,
                    Comment = person.Comment
                });
            
            var model = new PersonIndexViewModel { Items = items };

            /*Would I add LINQ here, and use the sam "model" variable, as above? As you add the "model" var to the "PrepareView(model) view below. So, I can create multiple bindings to a page.*/
            
            return PrepareView(model);
        
        }

    }



}


Such as binding 2+ grids to a page…


var model = new PersonIndexViewModel
{
Items = items,
Others = others
};
/* just add it to the existing model (of course, you'll need to edit the model definition also */
 
 
return PrepareView(model);

Alright, that makes sense. This isn’t as scary as I thought it would be. Learning a new architecture can be daunting, but 6 months from now, I will be able to help people out with their MVC questions :wink:

Time to strain my eyes with dark/light contrast with the IDE!

What persistence engine are you planning on using? Now that you’re a bit more familiar with mvc, seeing a more complete app (with comments) might ease the task.

Also, here are some things I consider to be good practice tips, with reasons.

  1. Don’t pass entities to your views.

Depending on how you’ve scoped your persistence context / session, it will most likely have been disposed before the view is rendered. If you’ve used lazy load to iterate through child collections on an entity in a view, it will throw an exception. Not passing entities into a view also reduces the chances of accidental changes to the back end.

  1. Use a view model specific to your view.

If you only need to see name information on a contact list, but not phone and address (wating to save that for a detail veiw), then simply don’t include the extra information in your view model definition. View models are similar to a db View into a given Table, showing just what you want, and how you want it.

  1. Use a master view model to pass things common to all pages into your _layout template.

Self explanatory.

  1. Keep your controllers clean.

If you find a given action’s code becomming large and spaghetti like, offload it to a service class.

  1. Familiarize yourself with inversion of control, and use that to inject interfaced objects into your controllers, services and repositories.

Being able to remove the default ctors from these objects also means you can remove the “using” clauses needed to make them work, decoupling your objects from the interfaced objects.

  1. Use automapper to map your entities to view models.

Mapping complex view models can generate a lot of looped code. This can get messy real quick. Setup simple this-to-that maps with automapper and get your models later with a single call.

  1. Separate your application layers!

Remember how I had other namespaces in the samples “Models” folder for domain and persistence? In practice, these would be in their own projects and referenced from the mvc app.

  1. Use the Authorize attribute.

Regardless of whether or not you’re using built in membership, or rolled your own using IPrincipal and IIdentity, use this attribute to quickly resolve the current users rights to an action.

There’s a lot more, but for now, these should help you out. Don’t fret if one or more of the above are alien right now. You’ll run across things in your own explorations.

Some of that makes sense, while some of it looks like Mandarin . lol. Like you said, I will learn as I go. Quick question, why do you Overload this method:


 private IPersonRepository personRepository;

        public PersonController(IPersonRepository personRepository)
        {
            this.personRepository = personRepository;
        }

        public PersonController()
        {
            // do really do this in production, use IoC
            this.personRepository = new PersonRepository();
        }


Do I have to overload every page?

And I take this pice of code is to connect to the data-layer/EF?


this.personRepository = new PersonRepository();

And I will defiently come back to this thread to remember those tips, hell, I’m just going to copy-and-paste it!

EDIT: I’m going to persistent with the URL or TempData…

Thanks

Good questions!

You noticed my original comment in the “default” ctor that said “do not do this is production”? This relates to #5 on my list above.

First, one of the goals of good programming (not just mvc) is decoupling consuming classes from the objects they consume. That means, the consuming class (the controller in this case), should only care about the consumed classes interfaces.

Second, you should eventually use an IoC container to resolve these dependencies, eliminating the need for a default ctor. Leaving just the one with the repository interface as a parameter.

Third, this is also important during testing, where you might want to substitute a mock class built on the same interface!

On the question of the repository class: yes, repositories are wrapper classes used to delegate complex queries to an underlying provider. Whether that is EF, L2S, or NH is unimportant. They abstract away the needs to know exact details:

var people = personRepository.GetWhereNameLike(“sam”);

:should return the same thing, regardless of which provider you are using.

One thing to note here is that when you introduce IoC into your app, and remove that “default” ctor, you won’t have that line:

this.personRepository = new PersonRepository();

That line is bad because it couples the controller with knowledge of implementation.

[EDIT]

I just realised I left out the word “not” in my original code comment. Sorry if that confused!

Tell you what I’m going to do…

Since you’ve made the leap, I want you to get it right from the beginning. Therefore, I will provide a simple solution that details each element with comments and thoughts. Maybe others will find it usefull as well. I will return shortly with a link (it’ll probably be to big to upload here).

Here is something I wrote for another member, as an example. It shows probably more than you want or need at this point, but it’s a good way to see how things work in a larger application (even though I only provided the foundation and basic account stuff). In this sample I didn’t have a need for a master model, so over look that. The rest should be informative.

http://fluentengine.com/downloads/example.zip

Right off the bat, I followed three related classes/pages: AccountRegisterModel.cs, AccountController.cs, and Register.cshtml. After scanning the pages up-and-down I realized how it all goes together, as far as MVC. I plan on going through your other classes, Domain and Enterprise Foundation, which is where they are called from the Controller.

This is f’n awesome man. Im finally seeing how this pattern works. I’m going to go over this some more for a couple of hours to learn as much as I can.

SP, give SerenaRules member of the Year!

Thanks dude. Just helping fill the gap left behind by my retirement. =)

Let me know if you have any questions. Keep in mind, that solution uses DDD-Lite (not full blown DDD) and there are no unit tests. We’ll cover those much later on.