SitePoint Sponsor

User Tag List

Results 1 to 19 of 19
  1. #1
    SitePoint Guru Jason__C's Avatar
    Join Date
    Oct 2009
    Location
    Racoon City
    Posts
    660
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    Smile 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.

  2. #2
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    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.

    http://msdn.microsoft.com/en-us/libr...ionresult.aspx

    ::smile::

  3. #3
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    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.

  4. #4
    SitePoint Guru Jason__C's Avatar
    Join Date
    Oct 2009
    Location
    Racoon City
    Posts
    660
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    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.

  5. #5
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    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. =)

  6. #6
    SitePoint Guru Jason__C's Avatar
    Join Date
    Oct 2009
    Location
    Racoon City
    Posts
    660
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    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

  7. #7
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    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.
    Attached Files Attached Files

  8. #8
    SitePoint Guru Jason__C's Avatar
    Join Date
    Oct 2009
    Location
    Racoon City
    Posts
    660
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Yeah, it's so much easier to understand when you actually have it in front of you!

    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.

  9. #9
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    You're welcome Chaser. =)

  10. #10
    SitePoint Guru Jason__C's Avatar
    Join Date
    Oct 2009
    Location
    Racoon City
    Posts
    660
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Morning SerenaRules, I added my question to the code, to make it easer to understand:
    Code Csharp:
     
    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....

  11. #11
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    Code Csharp:
    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);

  12. #12
    SitePoint Guru Jason__C's Avatar
    Join Date
    Oct 2009
    Location
    Racoon City
    Posts
    660
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    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

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

  13. #13
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    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.

    2) 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.

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

    Self explanatory.

    4) Keep your controllers clean.

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

    5) 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.

    6) 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.

    7) 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.

    8) 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.

  14. #14
    SitePoint Guru Jason__C's Avatar
    Join Date
    Oct 2009
    Location
    Racoon City
    Posts
    660
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    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:



    Code Csharp:
     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?

    Code Csharp:
    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

  15. #15
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    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!

  16. #16
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    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).

  17. #17
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    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

  18. #18
    SitePoint Guru Jason__C's Avatar
    Join Date
    Oct 2009
    Location
    Racoon City
    Posts
    660
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    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!

  19. #19
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    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.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •