tags:

views:

69

answers:

3

I am currently developing a sample application in MVC. The specification is as follows:
1. Single partial view for a grid.
2. The model will be stringly typed to a list of domain objects so that I can pass anything to the grid
3. I can drop this view into any page and decorating the controller with some attributes I can handle typical page next/prev actions.
4. The paging actions will be handed off to another controller and passed back to the main controller.

I suppose my question would be, How do I hand off control to another controller within the HandleUnknownAction and come back with a result from this other controller to populate my model in the caller

Hope this makes sense

Rich

A: 

I'd suggest using TempData to handle passing the result back due to it's limited life.

The handing off control to another controller could be done in a myriad of ways.

One might be

Redirect("/Controller/Action/SomePrametersToKnow/WhereToReturnTo")

Another could be:

var controller - new OtherController();
 controller.ActionMethod(parameters)

HTH,

Dan

Daniel Elliott
Hi DanTHanks for your reply. My issue is that I handle grid requests in the HandleUnknownAction. I would like to passs this request off to the GridController and return a GridModel from this call to then update the GridModel in my current controller. I think I am getting confused as to where to store the GridModel. Really I want it to be stored IN the gridController and then have access to it when needed in the caller controller. My goal is to have all requests for grid requests handled by the gridcontroller via ANY controller.I hope I am making sense
A: 

If I understand you correctly, you need to reuse the same logic from Grid controller in several other controllers. This is because you are placing the partial view into different aspx pages that are handled by different controllers. This partial view however should be served by the Grid Controller and specific controller which produced this view. For instance you have HomeController wich has Index method that is presenting Index.aspx with your grid in it. When user presses gird page button, it should launch HomeController HandleUnknownAction method, which should use the GridController logic to serve the paging request.

If this is right, I would just derrive in all my controllers from GridController and then I would make the paging method virtual one so you can override it HomeController.

For instance: GridController has method public virtual ActionResult NextPage()". HomeController has:

public override ActionResult NextPage()
{
  base.NextPage();
}

This way you can run both controllers without too much additional coding.

maciejgren
Favor composition over inheritance - forcing another layer into the inheritance hierarchy to provide access to logic that may or may not relate to the purpose of the leaf class is a design smell. Your solution says that HomeController is a GridController, but the design says that the HomeController contains an action that displays a grid - two very different statements.
Neal
A: 

I think you are trying to use the wrong aspect to do the work which is why you are finding it painful to achieve.

Getting the entire collection of objects and then rendering a subset of them is incredibly wasteful, how do you know your user is going to look at all the pages? What if there are 10,000 objects?

You want to retrieve 2 pieces of information from the database, the first page of objects and a count of the total number of objects - this should be handled from your controller ( preferably by calling into some form of service rather than directly to the database but thats an argument for another day ). Package that information into a class that contains any rendering helpers you need and send that class to the view. Given the object count and the page size, you can build simple navigation links which post back to the action passing the new page number.

public class GridView<T>
{
  private Dictionary<string, Func<T, string> _columnMap;

  public GridView( IEnumerable<T> items, long totalItems, long currentPage )
  {
    Items = items;
    Count = totalItems;
    CurrentPage = currentPage;
  }

  public long Count { get; private set; }
  public long CurrentPage { get; private set; }
  public IEnumerable<string> Columns { get; }
  public IEnumerable<T> Items { get; private set; }

  public void AddColumn( string name, Func<T, string> data )
  {
    _columnMap.Add( columnName, data );
  }

  public string GetColumnValue( string name, T item )
  {
    var valueExtractor = _columnMap[name];
    return valueExtractor(item);
  }

  public string GetPageCount()
  {
    // Calculate page count, convert to string and return
  }

  // Might be easier to make these two extension methods for the html helper class
  // so that you get easier access to the context of the current action
  public string GetPreviousLink()
  {
  }

  public string GetNextLink()
  {
  }
}

In the controller ...

// Use the route definition to set the page to default to 1
public ActionResult ShowProducts( int page )
{
  // Get the list of products for the requested page
  var currentPageData = ...
  // Get the total number of products
  var productCount = ...
  var gridData = new GridView<Product>( currentPageData, productCount, page );
  gridData.AddColumn( "Name", p => p.ProductName );
  gridData.AddColumn( "Price", p => p.Price.ToString("c") );
  gridData.AddColumn( "In Stock", p => p.StockLevel.ToString());

  return View( gridData );
}

Somewhere in the view ...

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

In the partial ...

<table>
<tr>
<% foreach( var column in Model.Columns ) { %>
  <th><% =column %></td>
<% } %>
</tr>
<% foreach( var item in Model.Items ) { %>
  <tr>
  <% foreach( var column in Model.Columns ) { %>
    <td><% =Model.GetColumnValue( column, item ) %></td>
  <% } %>
  <tr>
<% } %>
<tr>
  <td colspan="<% =Model.Columns.Count %>">Showing Page <% =Model.CurrentPage %> of <% =Model.GetPageCount() %></td>
</tr>
<tr>
  <td colspan="<% =Model.Columns.Count %>"><% =Model.GetPreviousLink() %> | <% =Model.GetNextLink() %></td>
</tr>
</table>

Please note, all code shown is aircode and is not guaranteed to work, merely demonstrate a series of concepts.

Neal