4

I am trying to upload a csv file and implement the CSVHelper competent using MVC3.

https://github.com/JoshClose/CsvHelper

I haven't found an example of using this with a file upload. Basically, I need to take the the CSV file and map to entity objects and save to the DB. Here are my entities:

public class SurveyEmailListModels
    {
        [Key]
        public int SurveyEmailListId { get; set; }

        [CsvField(Index = 0)]
        public int ProgramId { get; set; }

        [CsvField(Index = 1)]
        public virtual SurveyProgramModels SurveyProgramModels { get; set; }

        [CsvField(Index = 2)]
        public string SurveyEmailAddress { get; set; }

        [CsvField(Index = 3)]
        public bool SurveyResponded { get; set; }

    }

Upload Handler:

 [HttpPost]
        public ActionResult Upload(HttpPostedFileBase file, SurveyEmailListModels surveyemaillistmodels, int id)
        {
            if (file != null && file.ContentLength > 0)
            {


                // Collect file and place into directory for source file download

                var appData = Server.MapPath("~/csv/");
                var filename = Path.Combine(appData, Path.GetFileName(file.FileName));
                file.SaveAs(filename);



             //   surveyemaillistmodels.SurveyEmailAddress = "test@test.com";
             //   surveyemaillistmodels.SurveyResponded = true;
              //  surveyemaillistmodels.ProgramId = id;


                db.SurveyEmailListModels.Add(surveyemaillistmodels);
                db.SaveChanges();

                return Content(filename);

            }
            return Json(true);
        }

I'm not sure how to loop through the CSV file and save to the DB. Does anybody have an example?

Pieter Germishuys
  • 4,828
  • 1
  • 26
  • 34
user547794
  • 14,263
  • 36
  • 103
  • 152

1 Answers1

2

I would recommend you using a custom model binder for this purpose to avoid cluttering your controller logic with CSV parsing code:

public class SurveyEmailListModelsModelBinder: DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var csv = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        var file = ((csv.RawValue as HttpPostedFileBase[]) ?? Enumerable.Empty<HttpPostedFileBase>()).FirstOrDefault();

        if (file == null || file.ContentLength < 1)
        {
            bindingContext.ModelState.AddModelError(
                "", 
                "Please select a valid CSV file"
            );
            return null;
        }

        using (var reader = new StreamReader(file.InputStream))
        using (var csvReader = new CsvReader(reader))
        {
            return csvReader.GetRecords<SurveyEmailListModels>().ToArray();
        }
    }
}

which will be registered in Application_Start:

ModelBinders.Binders.Add(
    typeof(SurveyEmailListModels[]), 
    new SurveyEmailListModelsModelBinder()
);

And now we could have a controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Index(SurveyEmailListModels[] model)
    {
        if (!ModelState.IsValid)
        {
            return View();
        }

        ... store the model into the database 

        return Content("Thanks for uploading");
    }
}

and a view:

@Html.ValidationSummary()
@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    <input type="file" name="model" />
    <button type="submit">OK</button>
}
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Hi Darin, thanks for responding. I am trying to implement your solution and it I'm not seeing the CSV values inside the debugger. I set a break point on "return csvReader.GetRecords" and I'm not seeing any of the values. If I look under the "CSVReader" locals, I see "+ CurrentRecord 'csvReader.CurrentRecord' threw an exception of type 'CsvHelper.CsvReaderException' string[] {CsvHelper.CsvReaderException}" – user547794 Jun 18 '12 at 15:49
  • You will see the results if you put a breakpoint inside your controller action and inspect the model variable. Or simply use a temporary variable inside the model binder before returning to inspect. Also make sure you have read the CsvHelpers documentation for proper usage and expected format. For example your SurveyEmailListId property is not decorated with the `[CsvField(Ignore = true)]` attribute which might cause errors. If you have troubles using CsvHelpers please start a new question as this has nothing to do with ASP.NET MVC. – Darin Dimitrov Jun 18 '12 at 15:55
  • OK, I have started a new question here: http://stackoverflow.com/questions/11086942/using-csvhelper-on-file-upload – user547794 Jun 18 '12 at 16:20
  • What about this one? Do you still have some questions about the ASP.NET MVC part? – Darin Dimitrov Jun 18 '12 at 17:22
  • I can now see the values for the csv file in the debugger console, but nothing is showing up for the "model" variable when it hits the controller. Shouldn't it pass the array of values to the controller? – user547794 Jun 18 '12 at 18:33
  • One other things I noticed, all of the values are showing up as type in the array. Shouldn't they be typed the same as the entities in "SurveyEmailListModels"? How would I do that? – user547794 Jun 18 '12 at 19:22