0

I am working on an asp.net mvc web application and i have the folloiwng view:-

@model MvcApplication4.Models.SelectedCustomers
<h3>Select Customers Detials</h3>
@using (Html.BeginForm("Export", null))
{    <table>
        <tr>
            <th>
                NAME @Html.CheckBox("IncludeName", true)
            </th>
            <th>
                Description @Html.CheckBox("IncludeDescription", true)
            </th>
            <th>
                Address @Html.CheckBox("IncludeAddress", true)
            </th>
        </tr>
        @foreach (var item in Model.Info) {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.ORG_NAME)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.LOGIN_URI)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.SUPPORT_EMAIL)
                </td>
                @Html.HiddenFor(modelItem => item.ORG_ID)
            </tr>
        }
    </table>
    <p>
        @Html.ActionLink("Back","customer","Home") |
        <button type="submit" name="Format" value="xls">extract to excel</button> |
        <button type="submit" name="Format" value="csv">extract to text file</button>
    </p>
}

and the folloiwng action methods:-

[HttpPost]
        public ActionResult Export(ExportViewModel exportOptions, IList<AccountDefinition> accounts)
        {
          // var accounts = GetAccount();

            if (exportOptions.Format == "csv")
            {
                return accounts.AsCsvResult(exportOptions);
            }
            else if (exportOptions.Format == "xls")
            {
                return accounts.AsXlsResult(exportOptions);
            }

            throw new NotSupportedException(
                string.Format("Unsupported format: {0}", exportOptions.Format)
            );
        }

Then i defined the folloiwng method inside my AccountDefinition model:-

 public partial class AccountDefinition
    {
        public long ORG_ID { get; set; }
        public string ORG_NAME { get; set; }
        public string LOGIN_URI { get; set; }
        public string SUPPORT_EMAIL { get; set; }

        public virtual SDOrganization SDOrganization { get; set; }

    }
    public static class ActionResultextensions
    {
        public static ActionResult AsCsvResult(this IEnumerable<AccountDefinition> accounts, ExportViewModel exportOptions)
        {
            return new CsvResult(accounts, exportOptions);
        }

        public static ActionResult AsXlsResult(this IEnumerable<AccountDefinition> accounts, ExportViewModel exportOptions)
        {
            return new XlsResult(accounts, exportOptions);
        }
    }
}

Then i have the folloiwng model classes:-

1.

public class CsvResult : ExportAccountsResult
    {
        public CsvResult(IEnumerable<AccountDefinition> accounts, ExportViewModel exportOptions)
            : base(accounts, exportOptions)
        {
        }

        protected override string ContentType
        {
            get { return "text/csv"; }
        }

        protected override string Filename
        {
            get { return "accounts.csv"; }
        }        }

2,

 public class XlsResult : ExportAccountsResult
    {
        public XlsResult(IEnumerable<AccountDefinition> accounts, ExportViewModel exportOptions)
            : base(accounts, exportOptions)
        {
        }

        protected override string ContentType
        {
            get { return "application/vnd.ms-excel"; }
        }

        protected override string Filename
        {
            get { return "accounts.csv"; }
        }
    }

3.

public class ExportViewModel
    {
        public string Format { get; set; }
        public bool IncludeName { get; set; }
        public bool IncludeDescription { get; set; }
        public bool IncludeAddress { get; set; }

    }

4.

public abstract class ExportAccountsResult : ActionResult
    {
        protected ExportAccountsResult(IEnumerable<AccountDefinition> accounts, ExportViewModel exportOptions)
        {
            this.Accounts = accounts;
            this.ExportOptions = exportOptions;
        }

        protected IEnumerable<AccountDefinition> Accounts { get; private set; }
        protected ExportViewModel ExportOptions { get; private set; }

        protected abstract string ContentType { get; }
        protected abstract string Filename { get; }

        public override void ExecuteResult(ControllerContext context)
        {
            var response = context.HttpContext.Response;
            response.ContentType = ContentType;
            var cd = new ContentDisposition
            {
                FileName = this.Filename,
                Inline = false
            };
            response.AddHeader("Content-Disposition", cd.ToString());

            // TODO: Use a real CSV parser here such as https://github.com/JoshClose/CsvHelper/wiki/Basics
            // and never roll your own parser as shown in this oversimplified
            // example. Here's why: http://secretgeek.net/csv_trouble.asp
            using (var writer = new StreamWriter(response.OutputStream))
            {
                foreach (var account in this.Accounts)
                {
                    var values = new List<object>();
                    if (this.ExportOptions.IncludeName)
                    {
                        values.Add(account.ORG_NAME);
                    }
                    if (this.ExportOptions.IncludeDescription)
                    {
                        values.Add(account.LOGIN_URI);
                    }
                    if (this.ExportOptions.IncludeAddress)
                    {
                        values.Add(account.SUPPORT_EMAIL);
                    }
                    writer.WriteLine(string.Join(", ", values));
                }
            }
        }
    }

But when i run click on "extract to excel" or "extractt to text file" button inside the view i got the following exception :-

System.NullReferenceException was unhandled by user code
  HResult=-2147467261
  Message=Object reference not set to an instance of an object.
  Source=MvcApplication4
  StackTrace:
       at MvcApplication4.Models.ExportAccountsResult.ExecuteResult(ControllerContext context) in c:\Users\Administrator\Desktop\MvcApplication4\MvcApplication4\Models\ExportAccountsResult.cs:line 41
       at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
       at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass1c.<InvokeActionResultWithFilters>b__19()
       at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation)
  InnerException:

on the following code inside the ExportAccountsresults.cs:-

 foreach (var account in this.Accounts)
                {

:::UPDATED:::

I have updated my view to the following:-

@model MvcApplication4.Models.SelectedCustomers



<h3>Select Customers Detials</h3>

    @using (Html.BeginForm("Export", null))
    {
        Int32 c = -1;
        <table>
            <tr>
                <th>
                    NAME @Html.CheckBox("IncludeName", true)
                </th>
                <th>
                    Description @Html.CheckBox("IncludeDescription", true)
                </th>
                <th>
                    Address @Html.CheckBox("IncludeAddress", true)
                </th>
            </tr>
            @foreach (var item in Model.Info) {
                c++;
                <tr>
                    <td>
                        @Html.DisplayFor(modelItem => item.ORG_NAME)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.LOGIN_URI)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.SUPPORT_EMAIL)
                    </td>

                     <td>@Html.Hidden(String.Format("Info[{0}].ORG_ID", c), item.ORG_ID)</td>
                </tr>
            }
        </table>

        <p>
            @Html.ActionLink("Back","customer","Home") |
            <button type="submit" name="Format" value="xls">extract to excel</button> |
            <button type="submit" name="Format" value="csv">extract to text file</button>
        </p>
    }

And my action method to the following:-

  [HttpPost]
        public ActionResult Export(ExportViewModel exportOptions, SelectedCustomers sc)
        {
          // var accounts = GetAccount();
           var accounts = sc.Info.ToList();
            if (exportOptions.Format == "csv")
            {
                return accounts.AsCsvResult(exportOptions);
            }
            else if (exportOptions.Format == "xls")
            {
                return accounts.AsXlsResult(exportOptions);
            }

            throw new NotSupportedException(
                string.Format("Unsupported format: {0}", exportOptions.Format)
            );
        }

Now the csv file will open but it will be empty and it will noly contain ", ," for each record, for exmaple if there should be 7 records in the excel sheet or text file then the .csv file will have the following without data:-

, , 
, , 
, , 
, , 
, , 
, , 
, , 
  • it seems to mean that this.Accounts is Null. That is nothing from the request allow a binding to IEnumerable accounts. – tschmit007 Dec 26 '12 at 12:32
  • thanks can you check my update to the original question –  Dec 26 '12 at 12:36
  • can you post a line of html corresponding to a customer ? – tschmit007 Dec 26 '12 at 12:48
  • actually the SelectedCustomer object contains Ienumerable. i have added the related code to my original post. –  Dec 26 '12 at 12:51
  • 2
    I reformulate my question: what is the name of the hidden tag in the generated html. They should be SelectedCustomers.Info[x].ORG_ID, where x is a number from 0 to N-1 where N is the number of customer. – tschmit007 Dec 26 '12 at 12:56
  • thanks for the reply, but i only have hidden field for the ORG_ID as shown in the view code. there is no [X] number. –  Dec 26 '12 at 13:01
  • thnak can you chekc my :::UPDATED::: section the file will be opened but without data. –  Dec 26 '12 at 13:39
  • Almost all cases of `NullReferenceException` are the same. Please see "[What is a NullReferenceException in .NET?](http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-in-net)" for some hints. – John Saunders Dec 26 '12 at 14:03
  • 1
    @JohnPeter, well this is because you use Display and not Textbox. The only value transmitted is the ORG_ID. You should recover the data from the Ids before storing it. – tschmit007 Dec 26 '12 at 14:23

2 Answers2

0

The issue is that IList<AccountDefinition> is not convertible to IEnumerable<AccountDefinition>, so either receive an IEnumerable<T> here:

public ActionResult Export(ExportViewModel exportOptions, IList<AccountDefinition> accounts)

Or change everything else to an IList<T>.


One other approach would be this:

public ActionResult Export(ExportViewModel exportOptions, IList<AccountDefinition> accounts)
{
    var enumerableAccounts = accounts.AsEnumerable();

    if (exportOptions.Format == "csv")
    {
        return enumerableAccounts.AsCsvResult(exportOptions);
    }
    else if (exportOptions.Format == "xls")
    {
        return enumerableAccounts.AsXlsResult(exportOptions);
    }

    throw new NotSupportedException(
        string.Format("Unsupported format: {0}", exportOptions.Format)
    );
}
Mike Perrenoud
  • 66,820
  • 29
  • 157
  • 232
  • thanks for the reply i changed the action method to recevice IList accounts but i am still getting the following error:- “Object reference not set to an instance of an object.” –  Dec 26 '12 at 12:44
  • i also tried you action method , but still the same error is being raised. –  Dec 26 '12 at 12:46
  • @JohnPeter, effectively you need the same type from the action method all the way down. So, convert whatever you receive in the action method to `IEnumerable` and leave everything else the same as it was. Based on your two comments it feels like the rest of the stack has changed some now too. I'm sure you've been trying different things since you asked. – Mike Perrenoud Dec 26 '12 at 12:50
  • no i have the same code , but i tried your action method code. –  Dec 26 '12 at 12:53
  • 1
    @JohnPeter, I'm sure if you checked the value of `accounts` in the extension method it's `null` - can you verify that? – Mike Perrenoud Dec 26 '12 at 12:54
  • i have insert a break point an it seems you are right the value of the account is null for the AsCsvResult & AsXLsResult methods. why is this happening? thnaks –  Dec 26 '12 at 12:59
  • @JohnPeter, because what you're passing in (i.e. the variable you're using to access the extension method), isn't convertible to the type you're receiving in the extension method. – Mike Perrenoud Dec 26 '12 at 13:01
  • i do not understand well what is the problem ,, but how i can handle this? –  Dec 26 '12 at 13:02
  • 1
    Um... how is `IList` not convertible to `IEnumerable`? `IList` inherits from `IEnumerable`. – phoog Dec 26 '12 at 13:42
  • thnak can you check my :::UPDATED::: section the file now will be opened but without data. –  Dec 26 '12 at 13:43
0

the following runs (Pelase see in the view the difference between HiddeFor and Hidden):

the controller

namespace SomeApp.Controllers {
public class TestModel {        
    public IEnumerable<TestModelItem> Items { get; set; }
}

public class TestModelItem {
    public int Id { get; set; }
    public String Name { get; set; }
}
public class TestsController : Controller {

    public ActionResult Index() {
        TestModel tm = new TestModel {
            Items = new List<TestModelItem> {
                new TestModelItem { Id = 1, Name = "first"},
                new TestModelItem { Id = 2, Name = "second"}
            }
        };

        return View(tm);
    }

    [HttpPost]
    public ActionResult Index(TestModel tm) {
        return View(tm);
    }

}
}

And the view

@model SomeApp.Controllers.TestModel
@{
    Layout = null;
    Int32 c = -1;
}

<!DOCTYPE html>

<html>
<head>
    <title>Index</title>
</head>
<body>
    <form method="post">
        <table>
        @foreach (var i in Model.Items) {
            c++;
            <tr>
                @*<td>@Html.HiddenFor(x => i.Id)</td>*@
                <td>@Html.Hidden(String.Format("Items[{0}].Id", c), i.Id)</td>
                <td>@Html.DisplayFor(x => i.Name)</td>
            </tr>
        }
        </table>
        <button type=submit>go</button>
    </form>
</body>
</html>
tschmit007
  • 7,559
  • 2
  • 35
  • 43
  • thnak can you check my :::UPDATED::: section the file now will open but without data. –  Dec 26 '12 at 13:42