Convert a List to CSV in C# with ASP.NET MVC 5 and EF6

To convert a list of objects to CSV in classic ASP.NET MVC, query the records with Entity Framework 6, write a fixed header and correctly quoted values, then return the UTF-8 bytes with Controller.File. The student-report workflow includes a SQL Server table, an MVC view, and a Download CSV button.

The example uses ASP.NET MVC 5 and EF6 on the .NET Framework. It does not use ASP.NET Core MVC or EF Core. You will display RollNo, EnrollmentNo, Name, Branch, and University, then download those same columns in a stable order without relying on reflection.

Student CSV preview in Excel with RollNo, EnrollmentNo, Name, Branch, and University columns
The original student-report illustration. CSV stores the values and headers; it does not preserve Excel column widths or formatting.

Short Answer

Use a real CSV writer or quote each field and double embedded quotes. Materialize the permitted EF6 query before disposing its context, and return the CSV with a text/csv; charset=utf-8 content type and a download filename. Keep authorization and anti-forgery validation on the export action.

Prerequisites and platform

  • Windows, Visual Studio with the ASP.NET and web development workload, and a supported .NET Framework target such as 4.8.1 on a supported operating system.
  • An ASP.NET Web Application (.NET Framework) project using the MVC template, named Codingvila. Select Individual User Accounts for this demonstration, create an account, and sign in before opening the protected report. Existing applications can use their configured authentication instead.
  • SQL Server or LocalDB and Entity Framework 6 with its Database First designer. The EF designer creates the connection-string configuration when you select your actual database; do not paste database passwords into source code.

This is maintenance guidance for classic MVC. For a new ASP.NET Core application, use the ASP.NET Core CSV download example instead. The Microsoft MVC 5 and EF6 tutorial documents the project family; the general C# list-to-CSV guide covers file exports without an MVC database.

Step 1: Create the student table and sample records

Run this SQL in a demonstration database. The ten original sample students are retained. The explicit primary key lets EF6 identify each row reliably, and the nvarchar columns preserve Unicode names.

sql
CREATE TABLE dbo.Students
(
    Student_Id int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Students PRIMARY KEY,
    RollNo int NULL,
    EnrollmentNo nvarchar(15) NULL,
    Name nvarchar(50) NULL,
    Branch nvarchar(50) NULL,
    University nvarchar(50) NULL
);

INSERT INTO dbo.Students (RollNo, EnrollmentNo, Name, Branch, University)
VALUES
(101, N'14SOECE13017', N'Nikunj Satasiya', N'Computer Science Engineering', N'RK University'),
(102, N'14SOECE13018', N'Hiren Dobariya', N'Computer Science Engineering', N'RK University'),
(103, N'14SOECE13019', N'Vivek Ghadiya', N'Computer Science Engineering', N'RK University'),
(104, N'14SOECE13020', N'Shreya Patel', N'Information Technology', N'RK University'),
(105, N'14SOECE13021', N'Kishan Dave', N'Information Technology', N'RK University'),
(106, N'14SOECE13022', N'Meena Patel', N'Computer Science Engineering', N'RK University'),
(107, N'14SOECE13023', N'Niraj Patel', N'Civil Engineering', N'RK University'),
(108, N'14SOECE13024', N'Renu Savla', N'Computer Science Engineering', N'RK University'),
(109, N'14SOECE13025', N'Priya Dobariya', N'Information Technology', N'RK University'),
(110, N'14SOECE13026', N'Rinku Patel', N'Computer Science Engineering', N'RK University');

Step 2: Generate the EF6 model

  1. In the Codingvila project, right-click Models, choose Add > New Item > ADO.NET Entity Data Model, and select EF Designer from database.
  2. Choose the demonstration database connection and select dbo.Students. Name the generated context CodingvilaEntities and the entity Student; use the Codingvila.Models namespace. The code below uses those names exactly.
  3. Build the project. Confirm that Student_Id is the key and RollNo is nullable, matching the SQL table. The designer generates the entity and context, so do not create a second Student class.

The EF6 designer should mark only Student_Id as the key. The five CSV columns are an export layout, not a composite database key. EF Core uses a different model workflow.

If you need the designer steps in more detail, see create an EDMX model in ASP.NET MVC with EF6. Menu labels can vary by Visual Studio version.

Students table mapped by Entity Framework 6Student_Id is the only primary key and is an identity integer. RollNo is a nullable integer. EnrollmentNo, Name, Branch and University are nullable strings. The CSV report exports the five non-key columns in a fixed order.dbo.Students → EF6Student_Idint · primary keyRollNoint · nullableEnrollmentNonvarchar(15) · nullableNamenvarchar(50) · nullableBranchnvarchar(50) · nullableUniversitynvarchar(50) · nullable
Only Student_Id is the primary key. The generated EF6 model must match this SQL schema; the CSV uses RollNo, EnrollmentNo, Name, Branch and University.

Step 3: Add a CSV helper with a fixed column order

Create Models/StudentCsv.cs. The helper writes five columns for every row. It quotes each field, doubles embedded double quotes, uses CRLF record endings, and treats null text as an empty field. Numeric RollNo values use the invariant culture; negative numbers remain numeric.

csharp
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

namespace Codingvila.Models
{
    public static class StudentCsv
    {
        // The caller chooses whether this is a spreadsheet export or a lossless data export.
        public static string Create(IEnumerable<Student> students, bool protectSpreadsheetText)
        {
            if (students == null) throw new ArgumentNullException(nameof(students));

            var csv = new StringBuilder("RollNo,EnrollmentNo,Name,Branch,University\r\n");
            foreach (var student in students)
            {
                if (student == null) throw new ArgumentException("The sequence contains a null student.", nameof(students));

                string rollNo = student.RollNo.HasValue
                    ? student.RollNo.Value.ToString(CultureInfo.InvariantCulture)
                    : string.Empty;

                csv.Append(string.Join(",", new[]
                {
                    Quote(rollNo, false),
                    Quote(student.EnrollmentNo, protectSpreadsheetText),
                    Quote(student.Name, protectSpreadsheetText),
                    Quote(student.Branch, protectSpreadsheetText),
                    Quote(student.University, protectSpreadsheetText)
                }));
                csv.Append("\r\n");
            }

            return csv.ToString();
        }

        private static string Quote(string value, bool protectSpreadsheetText)
        {
            string text = value ?? string.Empty;
            if (protectSpreadsheetText && text.Length > 0 && IsFormulaPrefix(text[0]))
            {
                text = "\t" + text;
            }
            return "\"" + text.Replace("\"", "\"\"") + "\"";
        }

        private static bool IsFormulaPrefix(char value)
        {
            return value == '=' || value == '+' || value == '-' || value == '@'
                || value == '\t' || value == '\r' || value == '\n'
                || value == '\uFF1D' || value == '\uFF0B'
                || value == '\uFF0D' || value == '\uFF20';
        }
    }
}

Spreadsheet mode changes text. With protectSpreadsheetText: true, a formula-like text value gains a leading tab inside its quoted field. This is an Excel-oriented mitigation described by OWASP, not a universal guarantee for every spreadsheet or save-and-reopen workflow. The tab remains in the stored data. For machine-to-machine CSV, choose false to preserve the original values and apply the receiving system's validation policy. Quoting alone protects CSV structure, not spreadsheet execution.

Step 4: Add the MVC controller

Replace Controllers/HomeController.cs with the following. Both actions use the generated EF6 context. AsNoTracking avoids tracking read-only report entities; ToList executes the query while the context is still open.

csharp
using Codingvila.Models;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Web.Mvc;

namespace Codingvila.Controllers
{
    // Configure the MVC application's authentication before using this controller.
    [Authorize]
    public class HomeController : Controller
    {
        // A configurable product limit can replace this demonstration limit.
        private const int MaximumExportRows = 10000;

        public ActionResult Index()
        {
            using (var db = new CodingvilaEntities())
            {
                var students = db.Students.AsNoTracking()
                    .OrderBy(student => student.Student_Id)
                    .Take(MaximumExportRows + 1)
                    .ToList();
                return View(students);
            }
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult ExportToCSV()
        {
            using (var db = new CodingvilaEntities())
            {
                var students = db.Students.AsNoTracking()
                    .OrderBy(student => student.Student_Id)
                    .Take(MaximumExportRows + 1)
                    .ToList();

                if (students.Count > MaximumExportRows)
                {
                    return new HttpStatusCodeResult(400,
                        "The export exceeds 10000 rows. Filter the report first.");
                }

                // Spreadsheet mode intentionally prefixes formula-like text with a tab.
                string csv = StudentCsv.Create(students, protectSpreadsheetText: true);
                var encoding = new UTF8Encoding(true);
                byte[] bytes = encoding.GetPreamble()
                    .Concat(encoding.GetBytes(csv))
                    .ToArray();

                return File(bytes, "text/csv; charset=utf-8", "Students.csv");
            }
        }
    }
}

The header order is explicit, so the identity column and any navigation properties cannot accidentally become CSV columns. There is no trailing delimiter that would create a sixth empty field. UTF-8 preserves names such as Zoë Müller; the BOM can help Excel recognize the encoding. GetBytes does not add the BOM, which is why the code combines it with GetPreamble.

Export scope and size: the demonstration is a report of all sample students. In a real application, constrain both queries to records the signed-in caller may access. Sign-in alone does not implement tenant or record-level authorization. The 10,000-row limit is a demonstration safety cap, not a performance benchmark: the extra row detects an oversized result, and the action rejects it instead of silently truncating the CSV. The response is buffered in memory; use a filtered or background export for large reports.

Step 5: Add the Razor view

Create Views/Home/Index.cshtml. The form submits to the export action and includes an anti-forgery token. Razor encodes the student text when displaying it, so a value containing HTML is shown as text.

cshtml
@model System.Collections.Generic.IReadOnlyCollection<Codingvila.Models.Student>
@{
    ViewBag.Title = "Students";
}

<h2>Students</h2>
<p>The CSV export includes RollNo, EnrollmentNo, Name, Branch, and University.</p>
@if (Model.Count > 10000)
{
    <p>More than 10,000 rows match. Narrow the report before exporting.</p>
}

@using (Html.BeginForm("ExportToCSV", "Home", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    <button type="submit">Download CSV</button>
}

<table>
    <thead>
        <tr>
            <th>RollNo</th><th>EnrollmentNo</th><th>Name</th>
            <th>Branch</th><th>University</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var student in Model)
        {
            <tr>
                <td>@student.RollNo</td><td>@student.EnrollmentNo</td>
                <td>@student.Name</td><td>@student.Branch</td>
                <td>@student.University</td>
            </tr>
        }
    </tbody>
</table>

The System.Web.Mvc types in this project are different from Microsoft.AspNetCore.Mvc. Do not import both into this controller. Microsoft documents the MVC anti-forgery token and validation attribute; keep both sides of that pair.

Step 6: Run the report and check the CSV

  1. Start the application with Visual Studio, create or use a demonstration account, and sign in.
  2. Open /Home/Index. Confirm the ten students appear in the table.
  3. Select Download CSV. The browser receives Students.csv; the file is not written to a shared server folder.
  4. Open the file in a text editor before importing it into Excel. Check the five headers and one record per student.
csv
RollNo,EnrollmentNo,Name,Branch,University
"101","14SOECE13017","Nikunj Satasiya","Computer Science Engineering","RK University"
"102","14SOECE13018","Hiren Dobariya","Computer Science Engineering","RK University"

The block shows the header and first two records; the download includes all ten sample students. Test a name containing a comma, a double quote, a line break and a non-ASCII character. A value such as Zoë "Zee", Müller must remain one field, written as "Zoë ""Zee"", Müller". Also test null values, no students and a result above the export cap. An empty result produces a header-only CSV.

MVC report table showing the ten sample students and an Export to CSV button
Grid preview for the same ten student records. The application's styling can differ; the report columns and sample values match the tutorial.

Troubleshooting

  • Redirect to the sign-in page: the controller is protected by [Authorize]. Configure authentication and sign in; do not remove authorization from a private report.
  • Anti-forgery validation fails: submit the form generated by the view and keep @Html.AntiForgeryToken(). Do not fix the error by disabling validation.
  • CodingvilaEntities or Student is missing: finish the EF6 designer step, check the generated names and namespace, and rebuild. These are generated model types, not framework types.
  • The context cannot open the database: verify the designer-generated connection string, database availability and the application's database permissions. Log details server-side and show users a generic error; do not expose credentials.
  • Excel shows one column or changes leading zeros: use Excel's text/CSV import and choose the delimiter and column types explicitly. CSV cannot force Excel to preserve identifiers as text.

Conclusion

Keep the database query, displayed columns and CSV layout consistent. Use explicit headers, correct field quoting and UTF-8, and distinguish lossless data export from spreadsheet-oriented text mitigation. For a DataTable source or matching VB.NET examples, use the C# and VB.NET DataTable-to-CSV guide; keep this page for the classic MVC and EF6 student-report workflow.

Codingvila provides articles and blogs on web and software development for beginners as well as free Academic projects for final year students in Asp.Net, MVC, C#, Vb.Net, SQL Server, Angular Js, Android, PHP, Java, Python, Desktop Software Application and etc.

If you have any questions, contact us on info.codingvila@gmail.com