2016-12-05 3 views
0

Ich muss mehr als 35.000 Codes für eine Kampagne aus einer Excel-Tabelle importieren. Ich habe den folgenden Code unten (der genau so funktioniert, wie ich es brauche), aber wenn ich den Prozess ausführe, kann es über 20-30 Minuten dauern, um abzuschließen.Code-Optimierung beim Importieren von Excel-Tabellenkalkulation mit Epplus

Wenn es etwas gibt, das getan werden kann, um den Prozess zu beschleunigen, der sehr geschätzt würde. Ich würde mich selbst nicht als fortgeschrittenen Programmierer bezeichnen, und ich weiß, dass dies wahrscheinlich mit fortgeschrittenen Codierungstechniken gemacht werden könnte. Wenn mir jemand in die richtige Richtung zeigen könnte, wäre das sehr zu begrüßen.

Die Modelle für die Kampagnen- und Mastercode-Tabellen.

public class Campaign 
{ 
    public int CampaignId { get; set; } 
    public string Name { get; set; } 
    public virtual List<MasterCode> MasterCodes { get; set; } 
} 

public class MasterCode 
{ 
    public int MasterCodeId { get; set; } 

    public int CampaignId { get; set; } 
    public virtual Campaign Campaign { get; set; } 

    public string Code { get; set; } 
    public bool Used { get; set; } 
    public DateTime SubmittedOn { get; set; } 
} 

Folgendes ist der Code in der Ansicht. Die Formularfelder sind nicht modellgebunden, da sich dieser Code in einem Popupfenster einer Ansicht befindet, die bereits modellgebunden an ein anderes Modell gebunden ist.

@using (Html.BeginForm("UploadMasterCodes", "Campaigns", FormMethod.Post, new { enctype = "multipart/form-data" })) 
{ 
    @Html.AntiForgeryToken() 
    @Html.Hidden("CampaignId", Model.CampaignId) 

    <div class="form-group"> 
     @Html.Label("Master Code File") 
     @Html.TextBox("File", null, new { type = "file", @class = "form-control" }) 
    </div> 
} 

-Controller-Code

[HttpPost] 
[ValidateAntiForgeryToken] 
public ActionResult UploadMasterCodes(CampaignMasterCodeUploadViewModel model) 
{ 
    if (ModelState.IsValid) 
    { 
     var result = CampaignMethods.uploadMasterCodes(model.File, model.CampaignId); 
     TempData["SuccessMessage"] = result; 
     return RedirectToAction("Details", new { id = model.CampaignId }); 
    } 

    return RedirectToAction("Details", new { id = model.CampaignId }); 
} 

Und schließlich hier ist der Code, den Upload in die Datenbank durchführt. Ich baue eine Zeichenfolge, die alle Fehler verfolgen wird, da ich von ihnen allen wissen muss.

public static string uploadMasterCodes(HttpPostedFileBase file, int campaignId) 
    { 
     using (ApplicationDbContext _context = new ApplicationDbContext()) 
     { 
      string response = string.Empty; 

      var campaign = _context.Campaigns.Find(campaignId); 
      if (campaign == null) 
      { 
       return String.Format("Campaign {0} not found", campaignId.ToString()); 
      } 

      var submitDate = DateTime.Now; 
      int successCount = 0; 
      int errorCount = 0; 

      if ((file != null) && (file.ContentLength > 0) && !string.IsNullOrEmpty(file.FileName)) 
      { 
       byte[] fileBytes = new byte[file.ContentLength]; 
       var data = file.InputStream.Read(fileBytes, 0, Convert.ToInt32(file.ContentLength)); 

       using (var package = new ExcelPackage(file.InputStream)) 
       { 
        var currentSheet = package.Workbook.Worksheets; 
        var workSheet = currentSheet.First(); 
        var noOfRow = workSheet.Dimension.End.Row; 

        for (int i = 1; i <= noOfRow; i++) 
        { 
         var masterCode = new MasterCode(); 
         masterCode.Code = workSheet.Cells[i, 1].Value.ToString(); 
         masterCode.CampaignId = campaignId; 
         masterCode.Campaign = campaign; 
         masterCode.SubmittedOn = submitDate; 

         // Check to make sure that the Code does not already exist. 
         if (!campaign.MasterCodes.Any(m => m.Code == masterCode.Code)) 
         { 
          try 
          { 
           _context.MasterCodes.Add(masterCode); 
           _context.SaveChanges(); 
           successCount++; 
          } 
          catch (Exception ex) 
          { 
           response += String.Format("Code: {0} failed with error: {1} <br />", masterCode.Code, ex.Message); 
           errorCount++; 
          } 
         } 
         else 
         { 
          response += String.Format("Code: {0} already exists <br />", masterCode.Code); 
          errorCount++; 
         } 
        } 

        response += string.Format("Number of codes:{0}/Success: {1}/Errors {2}", noOfRow.ToString(), successCount.ToString(), errorCount.ToString()); 
       } 
      } 

      return response; 
     } 
    } 
+0

Ihr Code funktioniert und Sie wollen nur optimieren? Werfen Sie einen Blick auf diese Gemeinschaft: http://codereview.stackexchange.com/ – RandomStranger

+0

Ich wusste nicht einmal, dass das existiert. Der Code funktioniert. Ich bin gerade nach einer Überprüfung, um es zu beschleunigen .... Es ist ziemlich langsam. Vielen Dank – Andrew

Antwort

1

Auf Vorschlag von @grmbl, und viel lesen, konnte ich das Geschwindigkeitsproblem mit SQLBulkCopy lösen. Ich habe den OLEDB-Provider nicht verwendet, sondern stattdessen die Datei auf den Server kopiert und eine Datentabelle erstellt. Was ich dann für die Massenkopie verwendet habe. Der folgende Code hat die Laufzeit für 335.000 Datensätze auf etwa 10 Sekunden reduziert.

Ich habe alle Fehler Prüfcode ausgeschnitten, so ist es nicht eine Wand des Codes. Der Controller-Code.

[HttpPost] 
    [ValidateAntiForgeryToken] 
    public ActionResult UploadMasterCodes(CampaignMasterCodeUploadViewModel model) 
    { 
     if (ModelState.IsValid) 
     { 
      var filename = Path.GetFileName(model.File.FileName); 
      var path = FileMethods.UploadFile(model.File, Server.MapPath("~/App_Data/Bsa4/"), filename); 
      var dt = CampaignMethods.ProcessMasterCodeCsvToDatatable(path, model.CampaignId); 
      TempData["SuccessMessage"] = CampaignMethods.ProcessMastercodeSqlBulkCopy(dt); 
      return RedirectToAction("Details", new { id = model.CampaignId });     
     } 

     TempData["ErrorMessage"] = "Master code upload form error. Please refresh the page and try again."; 
     return RedirectToAction("Details", new { id = model.CampaignId }); 
    } 

Der Verarbeitungscode.

public static DataTable ProcessMasterCodeCsvToDatatable(string file, int campaignId) 
    { 
     using (ApplicationDbContext _context = new ApplicationDbContext()) { 

      var campaign = _context.Campaigns.Find(campaignId); 

      DataTable dt = new DataTable(); 
      dt.Columns.Add("CampaignId"); 
      dt.Columns.Add("Code"); 
      dt.Columns.Add("Used"); 
      dt.Columns.Add("SubmittedOn"); 

      string line = null; 
      var submitDate = DateTime.Now; 

      using (StreamReader sr = File.OpenText(file)) 
      { 
       while ((line = sr.ReadLine()) != null) 
       { 
        string[] data = line.Split(','); 
        if (data.Length > 0) 
        { 
         if (!string.IsNullOrEmpty(data[0])) 
         { 
          DataRow row = dt.NewRow(); 
          row[0] = campaign.CampaignId; 
          row[1] = data[0]; 
          row[2] = false; 
          row[3] = submitDate; 

          dt.Rows.Add(row); 
         } 
        } 
       } 
      } 

      return dt; 
     } 
    } 

    public static String ProcessMastercodeSqlBulkCopy(DataTable dt) 
    { 
     string Feedback = string.Empty; 

     using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString)) 
     { 
      cn.Open(); 
      using (SqlBulkCopy copy = new SqlBulkCopy(cn)) 
      { 
       copy.ColumnMappings.Add(0, "CampaignId"); 
       copy.ColumnMappings.Add(2, "Code"); 
       copy.ColumnMappings.Add(3, "Used"); 
       copy.ColumnMappings.Add(4, "SubmittedOn"); 
       copy.DestinationTableName = "MasterCodes"; 
       try 
       { 
        //Send it to the server 
        copy.WriteToServer(dt); 
        Feedback = "Master Code Upload completed successfully"; 
       } 
       catch (Exception ex) 
       { 
        Feedback = ex.Message; 
       } 
      } 
     } 

     return Feedback; 
    } 
+1

von 20-30 Minuten bis 10 Sekunden? Nett! ;) – grmbl

1

Jedes Mal, wenn ich brauche eine Excel-Datei Ich benutze diesen OLE DB-Provider zu lesen:

Provider=Microsoft.ACE.OLEDB.12.0;Data Source={FilePath};Extended Properties='Excel 12.0 Xml;HDR=Yes;IMEX=1'; 

(Weitere Informationen zu diesem Thema here und here gefunden werden können)

Dann könnten Sie Tun Sie eine für den schnellsten Einsatz. Siehe this answer wie Sie dies im Speicher tun. In Ihrem Fall müssen Sie zuerst die Datei irgendwo auf dem Server speichern, den Fortschritt verarbeiten und dem Benutzer mitteilen.

+0

Oh schön .... danke haufen. Ich fange an zu lesen. – Andrew