46

Perhaps this is just completely wrong, but back in the days of Webforms you would return a Dataset which you would then bind to a grid. But now in MVC you're not supposed to pass a datatable because you cannot serialize it and it's technically passing objects into the View where it doesn't belong? But how on earth am I meant to display data on a view?! I can't use LINQ to SQL classes here since this is a pure in memory data structure.

Ideally I'd just like to able to have an object which I can iterate within the view.

I'm really at a bit of a loss I have read the article from the "Gu" and I can only summarize that I have to pass back a ViewData Object instead?? Am I going nuts here?

Cheers from Blighty

Jon

JonathanTien
  • 1,386
  • 1
  • 11
  • 13

3 Answers3

75

This is not "wrong" at all, it's just not what the cool guys typically do with MVC. As an aside, I wish some of the early demos of ASP.NET MVC didn't try to cram in Linq-to-Sql at the same time. It's pretty awesome and well suited for MVC, sure, but it's not required. There is nothing about MVC that prevents you from using ADO.NET. For example:

Controller action:

public ActionResult Index()
{
    ViewData["Message"] = "Welcome to ASP.NET MVC!";

    DataTable dt = new DataTable("MyTable");
    dt.Columns.Add(new DataColumn("Col1", typeof(string)));
    dt.Columns.Add(new DataColumn("Col2", typeof(string)));
    dt.Columns.Add(new DataColumn("Col3", typeof(string)));

    for (int i = 0; i < 3; i++)
    {
        DataRow row = dt.NewRow();
        row["Col1"] = "col 1, row " + i;
        row["Col2"] = "col 2, row " + i;
        row["Col3"] = "col 3, row " + i;
        dt.Rows.Add(row);
    }

    return View(dt); //passing the DataTable as my Model
}

View: (w/ Model strongly typed as System.Data.DataTable)

<table border="1">
    <thead>
        <tr>
            <%foreach (System.Data.DataColumn col in Model.Columns) { %>
                <th><%=col.Caption %></th>
            <%} %>
        </tr>
    </thead>
    <tbody>
    <% foreach(System.Data.DataRow row in Model.Rows) { %>
        <tr>
            <% foreach (var cell in row.ItemArray) {%>
                <td><%=cell.ToString() %></td>
            <%} %>
        </tr>
    <%} %>         
    </tbody>
</table>

Now, I'm violating a whole lot of principles and "best-practices" of ASP.NET MVC here, so please understand this is just a simple demonstration. The code creating the DataTable should reside somewhere outside of the controller, and the code in the View might be better isolated to a partial, or html helper, to name a few ways you should do things.

You absolutely are supposed to pass objects to the View, if the view is supposed to present them. (Separation of concerns dictates the view shouldn't be responsible for creating them.) In this case I passed the DataTable as the actual view Model, but you could just as well have put it in ViewData collection. Alternatively you might make a specific IndexViewModel class that contains the DataTable and other objects, such as the welcome message.

I hope this helps!

Kurt Schindler
  • 21,037
  • 4
  • 43
  • 48
  • So if I want to send the DataTable back in the post function the model will be either just empty DataTable or null.. either ways i cannot get the DataTable in the post function? – Nikitesh Jul 31 '14 at 09:22
  • A little pushback on "*w/ Model strongly typed as System.Data.DataTable*". I mean, sure, the `DataTable` is "strongly typed", but in the context of a "you should use a ViewModel" discussion, the strongly typed admonition refers to the properties hanging off of each object in the VM. Here, you lose that strong typing at `var cell`, and in a "full" MVC setup, `cell` w/sh/could be strongly typed (and *its type known to the View*) as well. (I realize this is zombie commenting a little; the answer is from 2010, and it does answer the OP well. Minor nitpick/clarification.) – ruffin Mar 20 '15 at 18:14
  • I put similar code to this inside of my API controller and then used NewtonSoft.Json to serialize the objects. Still works in 2018 if you need to use stored procedures with MSSQLServer. – Ryan Dines Jul 08 '18 at 13:45
42

Here is the answer in Razor syntax

 <table border="1" cellpadding="5">
    <thead>
       <tr>
          @foreach (System.Data.DataColumn col in Model.Columns)
          {
             <th>@col.Caption</th>
          }
       </tr>
    </thead>
    <tbody>
    @foreach(System.Data.DataRow row in Model.Rows)
    {
       <tr>
          @foreach (var cell in row.ItemArray)
          {
             <td>@cell.ToString()</td>
          }
       </tr>
    }      
    </tbody>
</table>
Chiramisu
  • 4,687
  • 7
  • 47
  • 77
ProVega
  • 5,864
  • 2
  • 36
  • 34
6

While I tried the approach above, it becomes a complete disaster with mvc. Your controller passing a model and your view using a strongly typed model become too difficult to work with.

Get your Dataset into a List ..... I have a repository pattern and here is an example of getting a dataset from an old school asmx web service private readonly CISOnlineSRVDEV.ServiceSoapClient _ServiceSoapClient;

    public Get_Client_Repository()
        : this(new CISOnlineSRVDEV.ServiceSoapClient())
    {

    }
    public Get_Client_Repository(CISOnlineSRVDEV.ServiceSoapClient serviceSoapClient)
    {
        _ServiceSoapClient = serviceSoapClient;
    }


    public IEnumerable<IClient> GetClient(IClient client)
    {
        // ****  Calling teh web service with passing in the clientId and returning a dataset
        DataSet dataSet = _ServiceSoapClient.get_clients(client.RbhaId,
                                                        client.ClientId,
                                                        client.AhcccsId,
                                                        client.LastName,
                                                        client.FirstName,
                                                        "");//client.BirthDate.ToString());  //TODO: NEED TO FIX

        // USE LINQ to go through the dataset to make it easily available for the Model to display on the View page
        List<IClient> clients = (from c in dataSet.Tables[0].AsEnumerable()
                                 select new Client()
                                 {
                                     RbhaId = c[5].ToString(),
                                     ClientId = c[2].ToString(),
                                     AhcccsId = c[6].ToString(),
                                     LastName = c[0].ToString(), // Add another field called   Sex M/F  c[4]
                                     FirstName = c[1].ToString(),
                                     BirthDate = c[3].ToDateTime()  //extension helper  ToDateTime()
                                 }).ToList<IClient>();

        return clients;

    }

Then in the Controller I'm doing this

IClient client = (IClient)TempData["Client"];

// Instantiate and instance of the repository 
var repository = new Get_Client_Repository();
// Set a model object to return the dynamic list from repository method call passing in the parameter data
var model = repository.GetClient(client);

// Call the View up passing in the data from the list
return View(model);

Then in the View it is easy :

@model IEnumerable<CISOnlineMVC.DAL.IClient>

@{
    ViewBag.Title = "CLIENT ALL INFORMATION";
}

<h2>CLIENT ALL INFORMATION</h2>

<table>
    <tr>
        <th></th>
        <th>Last Name</th>
        <th>First Name</th>
        <th>Client ID</th>
        <th>DOB</th>
        <th>Gender</th>
        <th>RBHA ID</th>
        <th>AHCCCS ID</th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.ActionLink("Select", "ClientDetails", "Cis", new { id = item.ClientId }, null) |
        </td>
        <td>
            @item.LastName
        </td>
        <td>
            @item.FirstName
        </td>
         <td>
            @item.ClientId
        </td>
         <td>
            @item.BirthDate
        </td>
         <td>
            Gender @* ADD in*@
        </td>
         <td>
            @item.RbhaId
        </td>
         <td>
            @item.AhcccsId
        </td>
    </tr>
}

</table>
Jonas Lincoln
  • 9,567
  • 9
  • 35
  • 49
Tom Stickel
  • 19,633
  • 6
  • 111
  • 113
  • What's the advantage of doing this over using an [automapper](https://github.com/AutoMapper/AutoMapper)? That is, `GetClient` here seems to be doing an automapper's work, just moved to the Controller, which is at least conventionally anti-pattern. That is, if you're translating a `DataSet`/`DataTable` to a Model, you're no longer really answering the OP. Instead, imo, you're reconverging on the typical MVC pattern. Either the `DataTable` *is* your Model, as in [Kurt's answer](http://stackoverflow.com/a/2246936/1028230) or you're translating. – ruffin Mar 20 '15 at 18:01
  • 1
    My answer is almost 4 years old, I certainly would not do any dataset/datatable work. – Tom Stickel Mar 21 '15 at 01:41