views:

127

answers:

3

I am just getting into .NET MVC2 (.NET in general even) and I am having a hard time getting familiar with the "flow" of things. The MVC framework, I get.. for the most part. The part that I am getting tripped up on is applying standard programming practices to .NET MVC.

For example

public ActionResult Index()
{
   var dataContext = new SiteContentDataContext();
   var data = from c in dataContext.SiteContents where c.Slug == "home-page" select c;
   // call/return getMainNav()
   // call/return getSubNav()
   return View(data);
}

public ActionResult SiteContent(string strSlug)
{
   var dataContext = new SiteContentDataContext();
   var data = from c in dataContext.SiteContents where c.Slug == strSlug select c;
   // call/return getMainNav()
   // call/return getSubNav()
   return View(data);
}

private void getSubNav()
{
   // get subnav records from db.
   // return subnav records.
}

private void getMainNav()
{
   // get main nav records from db.
   // return main nav records.
}

The Index and SiteContent view are identical except for the fact that the Index view uses a different master page. Both views have a subnav and a main nav that will be dynamic content from a database.

The question, finally, is how would I go about populating the getSubNav and getMainNav functions and second, how would I return that data to the view properly?

+1  A: 

Look into ViewModel objects, objects you create whose purpose in life is to carry data to and from your Views. The Models folder created for you by default in a new MVC project would hold exactly those classes.

You have options besides the ViewModel object methodology, but none are as clean. (The ViewData dictionary is around to help but it's not intended to be the primary means of providing data to your views.) Here's an example of how to set the Model property of the ViewData object to an instantiated, populated viewmodel instance:

    public ActionResult SiteContent(string strSlug) {
        SiteContentVM model = new SiteContentVM();
        SiteService siteService = new SiteService();
        model.Slug = siteService.GetALittleSlimyCreature(strSlug);
        model.List1 = siteService.GetList1();
        model.List2 = siteService.GetList2();
        ViewData.Model = model;
        return View();
    }

You can now create a strongly typed view (complete with intellisense) to reference any properties of your ViewModel object instance from within your view simply through the Model property:

<% foreach (var item in Model.List1) { %>
    <% Html.Encode(item.StringField) %> <!-- <= writing a property -->
    <% Html.RenderPartial("PartialNameHere", item); %> <!-- <= the equivalent of a usercontrol -->
<% } %>

As you see above the process of getting data from the database does not change for MVC. You do it just as you would in a normal webforms project. (...usually this involves instantiating a business logic or service class of some sort rather than actually writing the code in the MVC project.)

Enjoy MVC!

Tahbaza
You got there just before me. I would add that if the Master Page needs data then you have to strongly type the Master Page as well to get full use of the ViewModel but that does have the problem that all pages that use the master page will then need to be strongly-typed to that ViewModel or it's subclasses
Ian Johnson
@Ian ,, you are right ,,, but sometimes you need to give some data to the Master Page like Navigation Links, Page Title, etc. I find having a strongly typed Master Page makes things a little easier. King Wilder have a nice way of doing it where he uses a BaseViewModel with a BaseViewModelBuilder to simplify things in the controllers. you can check his Golf Tracker source code to see how he did it.http://www.mvccentral.net/Kit/Details/3/Golf_Tracker_Kit
Manaf Abu.Rous
I think you are right, I use a similar patterns of a single strongly-typed master page and all view models inherit from that base model (which does force you to use a view model) but it does make the work that bit harder if you are potentially using several different master pages that may require several different base models
Ian Johnson
A: 

Well ,,, i think what you are looking for here is Partial Views.

You can embed the MainNav & SubNav Views into your SiteContent View.

here's how this goes.

create your MainNav & SubNav as partial views.

in your SiteContent view use the Html.RendarPartial Method to include the other two views.

<%= Html.RenderPartial("MainNav", Model); %>

<%= Html.RenderPartial("SubNav", Model); %>

Now to the remaining part about how to get the data to the MainNav & SubNav views. Now is a good time to get familiar with ViewModels. View models are nothing but classes with some properties that you want to give to a view to display.

In your case i would create 3 view models.

  • SiteContentViewModel contains the content that will be displayed in your page.
  • MainNavViewModel contains the data that will be displayed insdie the MainNav.
  • SubNavVIewModel contains the data that will be displayed insdie the SubNav.

then i would include the MainNavViewModel & SubNavVIewModel inside the SiteContentViewModel.

(if you are sure that every SiteContent View will have a MainNav & a SubNav )

Now it's up to you to fill each view model with that data that you need.

here's how the code will look like.

public class SiteContentViewModel {

       public MainNavViewModel MainNav { get; set;}
       public SubNavVIewModel  SubNav { get; set;}
       // Any Other Data Needed In The SiteContent View (ex. PageTitle)
}

public class MainNavViewModel {
       // Any Data Needed In The MainNav View
}


public class SubNavVIewModel  {
       // Any Data Needed In The SubNav View
}

Now back to the Partial Views ,,, using the View Models we created we can include the partials like this.

<%= Html.RenderPartial("MainNav", Model.MainNav); %>

<%= Html.RenderPartial("SubNav", Model.SubNav); %>

one important thing is to make our views strongly typed.

  • SiteContent view of type SiteContentViewModel
  • MainNav view of type MainNavViewModel
  • SubNav vIew of type SubNavViewModel

and in your SiteContent action method you will do something like this

   // Initialize the ViewModels.
   SiteContentViewModel model = new SiteContentViewModel();
   model.MainNav = new MainNavViewModel();
   model.SubNav  = new SubNavVIewModel();

   // Get Data From DB and set the properties that you created in your view models.
   // examples. 
   model.PageTitle = // some data from db.
   model.MainNav.(Some Property) = // some data from db.
   model.SubNav.(Some Property ) = // some data from db.
   return View(model);

hope that helps ... for more information you can see this link

Manaf Abu.Rous
It would probably be better to use RenderAction instead of RenderPartial since he uses it in multiple Views.
Necros
@Necros ,,, u might be right but there's one problem with RenderAction that keeps me away from using it whenever i can. It's much slower !!"RenderAction, by definition, has to run the whole ASP.NET pipeline to handle what appears to the system to be a new HTTP request, whereas RenderPartial is just adding extra content to an existing view."More Info Here:http://forums.asp.net/p/1502235/3556534.aspx
Manaf Abu.Rous
@Manaf - It's better than having one more complex property in multiple view models you have to worry about. The difference may be a few microseconds, you won't notice that. It's the same thing people say about getting property directly vs. getting it by reflection ... of course reflection is slower, but you won't notice that unless you do it a million times.
Necros
A: 

You should look into DDD and TDD for ASP.NET MVC. For the looks of it you seem to be using Linq To Sql. I'm going to try to explain in a few words what I do to accomplish a good architecture.

Architecture

  1. DB Context
  2. Domain Model
  3. Repository Pattern

It's good practice not to tie your Database Context with your Controllers. What you want to do is have your Controllers call your Repository, which in turn will return your Model. Now here's the tricky part you must convert the DB Context Objects into your Model Objects.

Imagine you have an Products table which Linq To SQL will give you as the Products Class. That Products Class is part of the DB Context and what you want to do is alienate your context, in fact your Controllers won't even know it exists.

Why would I need a Model when I have Linq To SQL?

Well for starters LTS will regenerate all Objects everytime you change your Database meaning you wont have the ability to make change to the DB Context. And also you want to be able to use ASP.NET MVC 2 Annotations for validation and more.

Now create a Products Class for your Model

namespace MvcApplication.Models
{
    public class Product
    {
        public int Id { get; set; }

        [Required]
        [StringLength(10)]
        public string Name { get; set; }

        [Required]
        public string Description { get; set; }

        [DisplayName("Price")]
        [Required]
        [RegularExpression(@"^\$?\d+(\.(\d{2}))?$")]
        public decimal UnitPrice { get; set; }
    }
}

Now you see this Class is part of the Model totally disconnected from the DB Context. Now what we do next is create our Repository Class.

namespace MvcApplication.Repository
{
    public class AppRepository {

       DbContext _context = new DbContext();

       public IQueryable<Products> GetProducts()
       { 
           return from p in _context.Products
                  select new Product {
                       Name = p.Name,
                       UnitPrice = p.UnitPrice
                  }
       }
   }
}

Now in your Controller you just call GetProducts();

public ActionResult SiteContent(string strSlug)
{
   var repository = new AppRepository();
   return View(repository.GetProducts());
}

Pretty isn't it.

You can use AutoMapper to map your DB Context objects to your Model objects. http://automapper.codeplex.com/

JeremySpouken