using Gremlin_BlazorServer.Data.EntityClasses; using Gremlin_BlazorServer.Services; using Gremlin_BlazorServer.Services.GUClasses; using Microsoft.VisualBasic.FileIO; using System.Diagnostics; using System.Globalization; using System.Text; using static Gremlin_BlazorServer.Data.EntityClasses.Enums; namespace Gremlin_BlazorServer.Data.DBClasses { public static class GenericImporter { //Private members private static readonly DateTime FarInTheFuture = DateTime.Parse("2050-12-31t00:00:00.000000z", CultureInfo.CurrentCulture); private static Encoding encoding = Encoding.UTF8; private static TextFieldParser csvParser = new(Filepath, encoding, true); private static string filepath = string.Empty; internal static GremlinContext db = new(); //Public properties public static string Filepath { get => filepath; set { filepath = value; if (encoding != null) { TextFieldParser _csvParser = new(Filepath, encoding, true); _csvParser.SetDelimiters(Separators); _csvParser.HasFieldsEnclosedInQuotes = true; } } } public static string[] Separators { get => csvParser.Delimiters!; set { if (value.Length > 0) { csvParser.SetDelimiters(value); } } } public static bool DataHasHeadings { get; set; } public static Encoding Encoding { get => encoding; set { encoding = value; ResetParser(); } } ////Constructors //public GenericImporter(string filepath) //{ // Filepath = filepath; // _dataHasHeadings = true; // _encoding = FileIO.GetEncoding(Filepath); // TextFieldParser _csvParser = new(Filepath, _encoding); // _csvParser.SetDelimiters(Separators); // _csvParser.HasFieldsEnclosedInQuotes = true; //} //public GenericImporter(string filepath, Encoding encoding) //{ // Filepath = filepath; // _dataHasHeadings = true; // _encoding = encoding; // TextFieldParser _csvParser = new(Filepath, _encoding); // _csvParser.SetDelimiters(Separators); // _csvParser.HasFieldsEnclosedInQuotes = true; //} //public GenericImporter(string filepath, string[] separators) //{ // Filepath = filepath; // _dataHasHeadings = true; // _encoding = FileIO.GetEncoding(Filepath); // TextFieldParser _csvParser = new(Filepath, _encoding); // Separators = separators; // _csvParser.HasFieldsEnclosedInQuotes = true; //} //public GenericImporter(string filepath, Encoding encoding, string[] separators) //{ // Filepath = filepath; // _encoding = encoding; // _dataHasHeadings = true; // TextFieldParser _csvParser = new(Filepath, _encoding); // Separators = separators; // _csvParser.HasFieldsEnclosedInQuotes = true; //} //Public methods public static void SetFilepath() { //Filepath = FileIO.GetFilepathFromUser(); } public static void ResetParser() { if (Filepath != "") { TextFieldParser _newParser = new(Filepath, encoding); _newParser.SetDelimiters(Separators); _newParser.HasFieldsEnclosedInQuotes = true; csvParser = _newParser; } } public static bool Run(string filepath, string separator, string encoding) { GenericImporter.filepath = filepath.Replace(@"\\", @"\"); try { Encoding = Encoding.GetEncoding(encoding); } catch (Exception) { Encoding = FileService.GetEncoding(GenericImporter.filepath); } Separators = new string[] { separator }; return ImportFile(); } public static async Task RunAsync(string filepath, string separator, string encoding) { if (filepath is "" or null) { return false; } GenericImporter.filepath = filepath.Replace(@"\\", @"\"); try { Encoding = Encoding.GetEncoding(encoding); } catch (Exception) { Encoding = FileService.GetEncoding(GenericImporter.filepath); } Separators = new string[] { separator }; return await Task.Run(ImportFile); } public static string GuessSeparator(string filepath)//, string encoding) { if (filepath is "" or null) { return string.Empty; } string[] candidates = new string[] { "|", ",", ";" }; int numberOfCandidates = candidates.Length; int numberOfLinesToEvaluate = 100; int[,] score = new int[numberOfLinesToEvaluate, numberOfCandidates]; GenericImporter.filepath = filepath.Replace(@"\\", @"\"); //if (csvParser == null) //{ // try // { // Encoding = Encoding.GetEncoding(encoding); // } // catch (Exception) // { // Encoding = FileIO.GetEncoding(GenericImporter.filepath); // } //} using (csvParser) { string line; for (int i = 0; i < numberOfLinesToEvaluate; i++) { if (!csvParser.EndOfData) { line = csvParser.ReadLine()!; for (int j = 0; j < numberOfCandidates; j++) { score[i, j] = line.Split(candidates[j]).Length; } } } } List<(string, int, float)> scoreBoard = new(); //Item1 = Separator, Item2 = Score (Anzahl aufeinanderfolgender Zeilen mit gleicher Anzahl von Fields), Item3 = Count (durchschnittliche Anzahl von Fields in Zeile) int x; float average; for (int j = 0; j < numberOfCandidates; j++) { x = 0; average = 0; for (int i = 0; i < numberOfLinesToEvaluate - 1; i++) { if (score[i, j] == score[i + 1, j] && score[i, j] > 1) { x++; } average += score[i, j]; } average += score[numberOfLinesToEvaluate - 1, j]; average /= numberOfLinesToEvaluate; scoreBoard.Add((candidates[j], x, average)); } ResetParser(); return scoreBoard.Find(f => f.Item2 == scoreBoard.Max(x => x.Item2) && f.Item3 == scoreBoard.Max(x => x.Item3)).Item1; } public static bool ImportFile() //Ein (möglichst) generischer Importer //1. Dateipfad erfassen //2. Column <-> Property Mapping //3. Typ der zu importierenden Daten herausfinden //4. Daten einlesen, konvertieren, validieren, Metadaten setzen und alles in Liste(n) speichern //5. Datenliste(n) in DB speichern { if (filepath == "") { SetFilepath(); } if (filepath == "") { return false; } using (csvParser) { //für geneerischen Code: //int numberOfLines = File.ReadAllLines(filepath).Length; //Assembly Gremlin = Assembly.GetExecutingAssembly(); //dynamische Spaltenzuordnung in Dictonary speichern string[] fields = csvParser.ReadFields()!; Dictionary MappingDictionary = ReadMappingDictionaryFromFile(); Dictionary mappingTable = MapDataHeading(fields, MappingDictionary); //determine data type to be imported DataIdentificator dataIdentificator = new(mappingTable); List detectedDataTypes = dataIdentificator.Identify(); //check for detectedDataTypes = empty (possible when DataIdentificator.Identify(MustMatchAllQualifer = true) and no dataset matches all qualifiers of a type) if (detectedDataTypes.Count == 0) { Debug.WriteLine($"DataIdentificator.Identify() konnte die Datenart nicht bestimmen!{Environment.NewLine}Versuchen Sie den Import mit einer dezidierten Importerfunktion."); return false; } return detectedDataTypes[0] switch { "ProductLine" => ImportProductLine(csvParser, mappingTable), "AccountType" => ImportAccountType(csvParser, mappingTable), "SubMarket" => ImportSubMarket(csvParser, mappingTable), "Account" => ImportAccounts(mappingTable), "Contact" => ImportContacts(mappingTable), "LSAG" => ImportLSAG(mappingTable), "Product" => ImportProducts(mappingTable), "CustomDescription" => ImportCustomDescriptions(),// mappingTable); _ => false, }; } } public static bool ImportFile(string filepath) { GenericImporter.filepath = filepath; return ImportFile(); } private static bool ImportCustomDescriptions()//Dictionary mappingTable) { List cDsReadFromFile = new(2500); Encoding = Encoding.GetEncoding("UTF-8"); //Custom-Descriptions-CSV hat festes Encoding. using (csvParser) { // Skip the row with the column names: _ = csvParser.ReadLine(); while (!csvParser.EndOfData) { // Read current line fields, pointer moves to the next line. string[] fields = csvParser.ReadFields()!; CustomDescription ImportedCD = new() { ProductNumber = fields[0], OptionNumber = fields[1], Heading = fields[3], DescriptionText = fields[4], CoverletterText = fields[5], Notes = fields[6], DataModificationByUser = "Importer", DataStatus = Status.Active.ToString(), DataValidUntil = FarInTheFuture, DataVersionComment = "Initial Importer by CD-ImporterFomCsv", Products = new List(), Supplier = new() { AccountName = fields[2] is "" or "RB" ? "Agilent Technologies" : fields[2] } }; ImportedCD.DataCreationDate = ImportedCD.DataValidFrom = ImportedCD.DataModificationDate = DateTime.Now; ImportedCD.DataVersionNumber = 1; cDsReadFromFile.Add(ImportedCD); } //Eingelesenen Custum Desciptions in DB schreiben: //Check for 3PPs that not yet in the db. //Step 1a: Add 3PP-Products from CD-List to list //Step 1b: Add list to db. //Step 2: Add CDs to products. using (GremlinContext db = new()) { //Step 1a List thirdPartyProductsFromImportedCDs = new(); foreach (CustomDescription CD in cDsReadFromFile) { if (CD.Supplier.AccountName != "Agilent Technologies") { Product new3PPProduct = new() { CustomDescription = CD, HasBreakPrices = false, ListPrice = 0, ProductNumber = CD.ProductNumber, OptionNumber = CD.OptionNumber, ProductStatus = Status.Active.ToString(), SapLongDescription = "", SapShortDescription = "", Weight = 0, IntroductionDate = DateTime.Now.Date, BreakRangeFrom = 0, BreakRangeTo = 0, ProductLine = DbHelper.ResolveProductLine(db, "3P") }; new3PPProduct.CustomDescription.Supplier = DbHelper.ResolveAccountByName(db, new3PPProduct.CustomDescription.Supplier.AccountName); _ = MetaDataSetter.ForImport(new3PPProduct, "GenericImporter-Method", "Created at import from Custom Descriptions."); thirdPartyProductsFromImportedCDs.Add(new3PPProduct); } }; //Step 1b db.Products.AddRange(thirdPartyProductsFromImportedCDs); //Imports both the products and the associated custom descriptions! _ = db.SaveChanges(); // Produkt aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen. //Step 2 List importedCDsWithEFReferences = new(100000); List cDsWithoutEFReferences = new(100); //nur zur Kontrolle, wird nicht verwendet. List productsInDb = db.Products.ToList(); Account agilent = db.Accounts.Where(a => a.AccountName == "Agilent Technologies").Single(); foreach (CustomDescription cD in cDsReadFromFile) { if (cD.Products == null) { continue; } //Skip Desciptions, if it has been already imported above (as part from 3PP) if (thirdPartyProductsFromImportedCDs.Intersect(cD.Products).Any()) { continue; } //Establish EF Reference. If no PN/Opt found, then skip this custom description. //CD.Product = GetProduct(db, CD.ProductNumber, CD.OptionNumber); //ResolveXY-functions return null, if no match is found in db. cD.Products = productsInDb.Where(product => product.ProductNumber == cD.ProductNumber && product.OptionNumber == cD.OptionNumber).ToList(); if (cD.Products == null) { cDsWithoutEFReferences.Add(cD); continue; } //Establish EF Reference: If no Supplier-Account found, then skip this custom description (shouldn't happen), else set Supplier to Agilent (3PP-Supplier have been processed before and their products should not be part of this list). if (cD.Supplier == null) { cDsWithoutEFReferences.Add(cD); continue; } cD.Supplier = agilent; //prepare properties _ = MetaDataSetter.ForImport(cD, "GenericImporter-Method", "Initial import by CSV Importer (Function GenericImporter.ImportCustomDescriptions)"); //add to final list of CDs, that will go into the db. importedCDsWithEFReferences.Add(cD); } db.CustomDescriptions.AddRange(importedCDsWithEFReferences); _ = db.SaveChanges(); //Bestätigung senden Debug.WriteLine($"Es wurden {importedCDsWithEFReferences.Count} eigene Beschreibungen erfolgreich der Datenbank hinzugefügt.{Environment.NewLine}Es wurden {thirdPartyProductsFromImportedCDs.Count} 3PP-Produkte neu angelegt."); } return true; } } private static bool ImportProducts(Dictionary mappingTable) { List ProductsReadFromFile = new(ParseProductFile(mappingTable)); return InsertProducts(ProductsReadFromFile); } private static bool InsertProducts(List products) { using (GremlinContext db = new()) { List productLines = db.ProductLines.ToList(); foreach (Product product in products) { //var query = db.ProductLines // .Where(a => a.ProductLineAbbreviation == product.ProductLine.ProductLineAbbreviation) // .First(); //product.ProductLine = query; //product.ProductLine = ResolveProductLine(db, product.ProductLine.ProductLineAbbreviation); product.ProductLine = productLines.Find(x => x.ProductLineCode == product.ProductLine.ProductLineCode) ?? new ProductLine(); _ = MetaDataSetter.ForImport(product, "GenericImporter-Method"); } db.Products.AddRange(products); _ = db.SaveChanges(); //Bestätigung senden Debug.WriteLine($"Es wurden {products.Count} Produkte erfolgreich der Datenbank hinzugefügt."); return true; } } private static List ParseProductFile(Dictionary columnNumberOf) ///Importiert Produkt-Daten aus CSV (erstellt aus PriceSurfer-CPL) /// - Encoding: Latin1 /// - Separator = ; /// - fixe Spaltenzahl und -folge (Überschriften werden nicht untersucht/verwendet): /// 1) Position = ID/PK /// 2) Partnumber /// 3) Option /// 4) (Short) Description /// 5) Current Month Price (EUR) /// 6) ProductLineID /// 7) Status /// 8) (Long) Description ///Kommentarzeichen: # (hardcoded, string array) { List results = new(100000); using (csvParser) { while (!csvParser.EndOfData) { // Read current line fields, pointer moves to the next line. Product ImportedProduct = new() { ProductLine = new() }; string[] fields = csvParser.ReadFields()!; //Kontrolle, ob Trennzeichen in Daten vorhanden ist: if (fields.Length > 27) //27 ist der Normalfall { //Sonderfall "EnVisionTM G|2": //suche fields[x], das auf " G" endet und auf das field[x+1] mit "2 " beginnt, kombiniere die beiden und schiebe alle darauffolgenden Felder in diesem Datensatz eins nach links. for (int i = 0; i < fields.Length - 1; i++) //fields.Length - 1, um durch i+1 nicht aus dem Index zu laufen { if (fields[i].EndsWith(" G")) { if (fields[i + 1].StartsWith("2 ")) { fields[i] = fields[i] + fields[i + 1]; for (int j = i + 2; j < fields.Length; j++) { fields[j - 1] = fields[j]; } } } } } //NICHT ausgewertete Felder: //fields[0] = Position [in CPL] //fields[8] = Warranty //fields[10] = PH Code //fields[11] = PH Description //fields[15] = Country of Manufacturing //fields[16] = ECCL //fields[17] = M41 //fields[18] = First Supplier Code //fields[19] = Harmonized Tarif Schedule //fields[20] = Hazardous Good Flag //fields[21] = Order instructions //fields[23] = End of Production Date //fields[24] = End of Support Date ImportedProduct.ProductNumber = fields[columnNumberOf["ProductNumber"]]; if (fields[columnNumberOf["OptionNumber"]].Length == 4) //Optionsnummer mit führendem Apostroph { ImportedProduct.OptionNumber = fields[columnNumberOf["OptionNumber"]].Substring(1); //schneidet erstes Zeichen/Apostroph weg } else if (fields[columnNumberOf["OptionNumber"]].Length == 3) //3-stellige Optionsnummer übernehmen; keine Aktion bei leerem Feld (keine Optionsnummer) { ImportedProduct.OptionNumber = fields[columnNumberOf["OptionNumber"]]; } ImportedProduct.SapShortDescription = fields[columnNumberOf["SapShortDescription"]]; ImportedProduct.ListPrice = decimal.Parse(fields[columnNumberOf["ListPrice"]], new CultureInfo("de-de")); //parsing! compare with old value (either from CSV or from DB) -> price change? //if (fields[columnNumberOf["ListPrice"]] != "") // if (decimal.Parse(fields[columnNumberOf["ListPrice"]], new CultureInfo("de-de")) != ImportedProduct.ListPrice) // listpriceHasChanged = true; ImportedProduct.BreakRangeFrom = fields[columnNumberOf["BreakRangeFrom"]] == "" ? 0 : Convert.ToInt32(fields[columnNumberOf["BreakRangeFrom"]]); ImportedProduct.HasBreakPrices = ImportedProduct.BreakRangeFrom > 0; ImportedProduct.BreakRangeTo = fields[columnNumberOf["BreakRangeTo"]] is "" or "+" ? 0 : Convert.ToInt32(fields[columnNumberOf["BreakRangeTo"]]); //erfasst sowohl Produkte ohne Break-Preise ("") als auch "+" bei Mengenangaben a la "100+" (= von 100 bis unendlich) ImportedProduct.ProductLine.ProductLineCode = fields[columnNumberOf["ProductLineCode"]]; switch (fields[columnNumberOf["ProductStatus"]]) { case "Active": ImportedProduct.DataStatus = Status.Active.ToString(); ImportedProduct.ProductStatus = Status.Active.ToString(); break; case "New Product" or "New": ImportedProduct.DataStatus = Status.Active.ToString(); ImportedProduct.ProductStatus = Status.New.ToString(); break; case "Price Changed": ImportedProduct.DataStatus = Status.Active.ToString(); ImportedProduct.ProductStatus = Status.PriceUpdated.ToString(); break; default: ImportedProduct.DataStatus = Status.StatusUpdated.ToString(); break; } if (fields[columnNumberOf["Weight"]] != null) { ImportedProduct.Weight = ParseWeight(fields[columnNumberOf["Weight"]]); } if (fields[columnNumberOf["WeightUnit"]] == "G") { ImportedProduct.Weight /= 1000; //Umrechnung g in kg } if (fields[columnNumberOf["IntroductionDate"]] != "") { ImportedProduct.IntroductionDate = DateTime.Parse(fields[columnNumberOf["IntroductionDate"]]); } ImportedProduct.SapLongDescription = fields[columnNumberOf["SapLongDescription"]]; results.Add(ImportedProduct); } } return results; } private static float ParseWeight(string input) { StringBuilder sb = new(); if (input.StartsWith(".")) { input = sb.Append(0).Append(input).ToString(); } try { return float.Parse(input, new CultureInfo("en-us")); } catch (Exception ex) { Debug.WriteLine(ex, "Unhandled Error in Function 'ParseWeight'"); return 0; } } private static bool ImportLSAG(Dictionary mappingTable) { bool result; result = ImportAccounts(mappingTable); ResetParser(); _ = csvParser.ReadFields(); //Skip Heading result = result && ImportContacts(mappingTable); return result; } public static bool ImportContacts(Dictionary columnNumberOf) { List ContactsReadFromFile = new(8000); using (csvParser) { while (!csvParser.EndOfData) { Contact ImportedContact = new(); string[] fields = csvParser.ReadFields()!; //No conversion ImportedContact.AcademicTitle = fields[columnNumberOf["AcademicTitle"]]; ImportedContact.FirstName = fields[columnNumberOf["FirstName"]]; ImportedContact.LastName = fields[columnNumberOf["LastName"]]; ImportedContact.EMail = fields[columnNumberOf["EMail"]]; ImportedContact.Department = fields[columnNumberOf["Department"]]; ImportedContact.Room = fields[columnNumberOf["Room"]]; ImportedContact.PhoneNumber = fields[columnNumberOf["PhoneNumber"]]; ImportedContact.Function = fields[columnNumberOf["Function"]]; ImportedContact.MobileNumber = fields[columnNumberOf["MobileNumber"]]; //Convert Gender ImportedContact.Gender = fields[columnNumberOf["Gender"]] == "M" ? (byte)Gender.Male : fields[columnNumberOf["Gender"]] == "F" ? (byte)Gender.Female : (byte)Gender.Unknown; //Convert OptIn Status ImportedContact.OptInStatus = fields[columnNumberOf["OptInStatus"]] switch { "Opt In" => true, "Opt Out" => false, _ => false, }; //Convert "SAP Contact Number" try { ImportedContact.SAPContactNumber = Convert.ToInt32(fields[columnNumberOf["SAPContactNumber"]], CultureInfo.CurrentCulture); } catch (FormatException ex) { //errorhandling here: "Unable to parse input: " + field[0] + " as uint" //Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben string errorRaiser = fields[columnNumberOf["SAPContactNumber"]]; if (errorRaiser == "") { errorRaiser = "No Contact Number in this row!"; } DbHelper.DisplayErrorDetails(ex, errorRaiser); return false; } catch (OverflowException ex) { //errorhandling here: "Number cannot fit in an Uint." //Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben string errorRaiser = fields[columnNumberOf["SAPContactNumber"]]; if (errorRaiser == "") { errorRaiser = "No Contact Number in this row!"; } DbHelper.DisplayErrorDetails(ex, errorRaiser); return false; } //Convert "Account Created on" int year = Convert.ToInt32(fields[columnNumberOf["SAPContactCreationDate"]].Substring(0, 4), CultureInfo.CurrentCulture); int month = Convert.ToInt32(fields[columnNumberOf["SAPContactCreationDate"]].Substring(4, 2), CultureInfo.CurrentCulture); int day = Convert.ToInt32(fields[columnNumberOf["SAPContactCreationDate"]].Substring(6, 2), CultureInfo.CurrentCulture); ImportedContact.SAPContactCreationDate = new DateTime(year, month, day); //Convert "No Phone Calls" if (fields[columnNumberOf["NoPhoneCalls"]] == "1") { ImportedContact.NoPhoneCalls = true; } //Convert "No Hardcopy Mailing" if (fields[columnNumberOf["NoHardcopyMailing"]] == "1") { ImportedContact.NoHardcopyMailing = true; } //SAPAccountID in Contact.Notes speichern, um den entsprechenden Account aus DB heraussuchen zu können: ImportedContact.Notes = fields[columnNumberOf["SAPAccountNumber"]]; ContactsReadFromFile.Add(ImportedContact); } //Eingelesenen Account in DB schreiben: using (GremlinContext db = new()) { foreach (Contact contact in ContactsReadFromFile) { // AccountID aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen. contact.Account = DbHelper.ResolveAccountById(db, Convert.ToUInt32(contact.Notes)); contact.Notes = ""; _ = MetaDataSetter.ForImport(contact, "GenericImporter-Method"); } db.Contacts.AddRange(ContactsReadFromFile); _ = db.SaveChanges(); } } //Bestätigung senden Debug.WriteLine($"Es wurden {ContactsReadFromFile.Count} Contacts erfolgreich der Datenbank hinzugefügt."); return true; } public static bool ImportAccounts(Dictionary columnNumberOf) ///Importiert Account Daten aus CSV (erstellt aus LSAG_Contact_List_Tool.xlsx) /// /// - Encoding: Latin1/ISO-8859-1 /// - fixe Spaltenzahl und -folge (Überschriften werden nicht untersucht/verwendet): /// 1) SAPAccountNumber /// 2) AccountName /// 3) ZIP /// 4) City /// 5) Street /// 6) AccountSubMarketCode /// 7) AccountTypeCode /// 8) PhoneNumber /// 9) AccountsCreatedInSAP ///Kommentarzeichen: # (hardcoded, string array) ///Rückgabe 'false' bei Fehler oder User-Abbruch, ansonsten 'true'. /// ///Argumente: /// 1. { List AccountsReadFromFile = new(1000); while (!csvParser.EndOfData) { bool DataHasError = false; // Read current line fields, pointer moves to the next line. Account ImportedAccount = new(); { ImportedAccount.SubMarket = new(); ImportedAccount.AccountType = new(); ImportedAccount.Contacts = new List(); } string[] fields = csvParser.ReadFields()!; //Konvertierung erforderlich: try { ImportedAccount.SAPAccountNumber = Convert.ToUInt32(fields[columnNumberOf["SAPAccountNumber"]]); } catch (FormatException ex) { //errorhandling here: "Unable to parse input: " + field[0] + " as uint" //Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben string errorRaiser = fields[columnNumberOf["SAPAccountNumber"]]; if (errorRaiser == "") { errorRaiser = "No Account Number in this row!"; } DbHelper.DisplayErrorDetails(ex, errorRaiser); return false; } catch (OverflowException ex) { //errorhandling here: "Number cannot fit in an Uint." //Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben string errorRaiser = fields[columnNumberOf["SAPAccountNumber"]]; if (errorRaiser == "") { errorRaiser = "No Account Number in this row!"; } DbHelper.DisplayErrorDetails(ex, errorRaiser); return false; } try { ImportedAccount.ZIP = Convert.ToUInt32(fields[columnNumberOf["ZIP"]]); } catch (FormatException ex) { //errorhandling here: "Unable to parse input: " + field[0] + " as uint" //Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben string errorRaiser = fields[columnNumberOf["ZIP"]]; if (errorRaiser == "") { errorRaiser = "No ZIP in this row!"; } DbHelper.DisplayErrorDetails(ex, errorRaiser); return false; } catch (OverflowException ex) { //errorhandling here: "Number cannot fit in an Uint." //Zeilennummer, Feld und Wert in Liste speichern, um am Ende gesammelt auszugeben string errorRaiser = fields[columnNumberOf["ZIP"]]; if (errorRaiser == "") { errorRaiser = "No ZIP in this row!"; } DbHelper.DisplayErrorDetails(ex, errorRaiser); return false; } //Konvertierung für AccountCreatedinSAPOn Property: //Test auf Inhalt bzw. Eintrag in der Mapping-Table (optionales Feld) if (columnNumberOf.TryGetValue("AccountCreatedinSAPOn", out int columnNumber)) { if (fields[columnNumberOf["AccountCreatedinSAPOn"]].Length != 0) { int year = Convert.ToInt32(fields[columnNumber].Substring(0, 4)); int month = Convert.ToInt32(fields[columnNumber].Substring(4, 2)); int day = Convert.ToInt32(fields[columnNumber].Substring(6, 2)); ImportedAccount.AccountCreatedInSAPOn = new DateTime(year, month, day); } } //Convert City von Großschreibung zu Normalschreibung ImportedAccount.City = fields[columnNumberOf["City"]].First().ToString() + fields[columnNumberOf["City"]].Substring(1).ToLower(); //keine Konvertierung nötig: ImportedAccount.AccountName = fields[columnNumberOf["AccountName"]]; ImportedAccount.Street = fields[columnNumberOf["Street"]]; ImportedAccount.SubMarket.SubMarketCode = fields[columnNumberOf["SubMarketCode"]]; ImportedAccount.SubMarket.DataStatus = Status.Active.ToString(); ImportedAccount.AccountType.AccountTypeCode = fields[columnNumberOf["AccountTypeCode"]]; ImportedAccount.AccountType.DataStatus = Status.Active.ToString(); ImportedAccount.PhoneNumber = fields[columnNumberOf["PhoneNumber"]]; ImportedAccount.DataStatus = Status.Active.ToString(); //Validierungen: if (ImportedAccount.AccountName == "" || ImportedAccount.City == "" || ImportedAccount.Street == "" || ImportedAccount.SubMarket.SubMarketCode == "" || ImportedAccount.AccountType.AccountTypeCode == "" || ImportedAccount.SAPAccountNumber == 0) { DataHasError = true; } //Validierten Account der Liste hinzufügen: if (DataHasError == false) { _ = ImportedAccount.AddIfUniqueTo(AccountsReadFromFile); } } //Eingelesenen Account in DB schreiben: DateTime now = DateTime.Now; foreach (Account account in AccountsReadFromFile) { // AccountType aus DB laden, damit der Datensatz vom Context verfolgt wird und EF Core nicht versucht, diesen standardmäßig neu anzulegen. AccountType accountType = db.AccountTypes .Where(a => a.AccountTypeCode == account.AccountType.AccountTypeCode) .First(); account.AccountType = accountType; account.AccountType = DbHelper.ResolveAccountType(db, account.AccountType.AccountTypeCode); SubMarket subMarket = db.SubMarkets .Where(a => a.SubMarketCode == account.SubMarket.SubMarketCode) .First(); account.SubMarket = subMarket; account.SubMarket = DbHelper.ResolveSubmarket(db, account.SubMarket.SubMarketCode); _ = MetaDataSetter.ForImport(account, "GenericImporter-Method"); //account.DataVersionComment = "Initial import by CSV Importer (Function DbHelper.ImportAccountsFromCSV)"; } db.Accounts.AddRange(AccountsReadFromFile); _ = db.SaveChanges(); //Bestätigung senden Debug.WriteLine($"Es wurden {AccountsReadFromFile.Count} Accounts erfolgreich der Datenbank hinzugefügt."); return true; } public static bool ImportProductLine(TextFieldParser csvParser, Dictionary mappingTable) { //foreach line in file: //read seed data, parse/split, save to object with metadata //add object to list //add list to context, savechanges List productLinesReadFromFile = new(50); using (csvParser) { while (!csvParser.EndOfData) { ProductLine importedProductLine = new(); string[] fields = csvParser.ReadFields()!; importedProductLine.ProductLineCode = fields[mappingTable["ProductLineCode"]]; importedProductLine.ProductLineDescription = fields[mappingTable["ProductLineDescription"]]; productLinesReadFromFile.Add(importedProductLine); _ = MetaDataSetter.ForImport(importedProductLine, "GenericImporter-Method"); } } db.ProductLines.AddRange(productLinesReadFromFile); _ = db.SaveChanges(); return true; } public static bool ImportAccountType(TextFieldParser csvParser, Dictionary mappingTable) { //foreach line in file: //read seed data, parse/split, save to object with metadata //add object to list //add list to context, savechanges List accountTypesReadFromFile = new(20); using (csvParser) { while (!csvParser.EndOfData) { AccountType importedAccountType = new(); string[] fields = csvParser.ReadFields()!; importedAccountType.AccountTypeCode = fields[mappingTable["AccountTypeCode"]]; importedAccountType.AccountTypeDescription = fields[mappingTable["AccountTypeDescription"]]; accountTypesReadFromFile.Add(importedAccountType); _ = MetaDataSetter.ForImport(importedAccountType, "GenericImporter-Method"); } } db.AccountTypes.AddRange(accountTypesReadFromFile); _ = db.SaveChanges(); return true; } public static bool ImportSubMarket(TextFieldParser csvParser, Dictionary mappingTable) { //foreach line in file: //read seed data, parse/split, save to object with metadata //add object to list //add list to context, savechanges List subMarketsReadFromFile = new(20); using (csvParser) { while (!csvParser.EndOfData) { SubMarket importedSubMarket = new(); string[] fields = csvParser.ReadFields()!; importedSubMarket.SubMarketCode = fields[mappingTable["SubMarketCode"]]; importedSubMarket.SubMarketDescription = fields[mappingTable["SubMarketDescription"]]; subMarketsReadFromFile.Add(importedSubMarket); _ = MetaDataSetter.ForImport(importedSubMarket, "GenericImporter-Method"); } } db.SubMarkets.AddRange(subMarketsReadFromFile); _ = db.SaveChanges(); return true; } //private static void SetProperty(object gremlinEntity, PropertyInfo propertyInfo, string value) //{ // propertyInfo.SetValue(gremlinEntity, value); //} //private static Array GetNewGremlinTypeArray(Assembly assembly, string detectedDataType, int length) //{ // Type GremlinType = GetNewGremlinType(assembly, detectedDataType).GetType(); // //Create an one-dimensional array with n+1 or n+2 elements (n = number of datasets, plus heading and/or emptyline at EOF): // return Array.CreateInstance(GremlinType, length); // //Das geht alles nicht: // //List test = new(); // //List test = new(); // //List test = new(); // ////Das geht: // ////Create iList: // //Type listType = typeof(List<>).MakeGenericType(GremlinType); // //var list = (IList)Activator.CreateInstance(listType); // //list.Add(new Account() { AccountName = "Test" }); // //list.Add(GremlinClassObject); //} //private static object GetNewGremlinType(Assembly Gremlin, string detectedDataType) //{ // return Activator.CreateInstance(Gremlin.ToString(), "Gremlin." + detectedDataType).Unwrap(); //} public static Dictionary ReadMappingDictionaryFromFile() { Dictionary result = new(); string fileInput = FileService.ReadResource("MappingDictionary.txt"); string[] lines = fileInput.Split(Environment.NewLine); foreach (string line in lines) { string[] fields; fields = line.Split("|"); result.Add(fields[0], fields[1]); } return result; } public static Dictionary MapDataHeading(string[] headings, Dictionary columnPropertyMapping) { Dictionary result = new(); for (int i = 0; i < headings.Length; i++) { string heading = headings[i].ToLower(CultureInfo.CurrentCulture) .Trim() .Replace(" ", "") .Replace("-", "") .Replace("_", "") .Replace(".", "") .Replace(":", ""); if (columnPropertyMapping.TryGetValue(heading, out string? value)) { result.Add(value, i); } } return result; } //private static void BasimodosCodeDump() //{ // //Unfertiger, generischer Code aus der ImportFile() Methode. // //Aufbewahren für später, wenn Zeit ist, das ordentlich zu machen. // //while (!csvParser.EndOfData) // //{ // // //read // // //new data instance // // //convert // // //validate // // //set metadata: SetMetadataForImport(IMetadata) // // //add to list // // fields = csvParser.ReadFields(); // Read current line fields, pointer moves to the next line. // // if (fields[0] == "") break; // Am Ende hängt eine leere Zeile, die im Parser einen Fehler auslösen würde. // // //TO DO: vor der foreach "Account" bevorzugt behandeln (Sonderfall bei LSAG: Liste deduplizieren, und als erstes importieren, da sonst keine Contacts importiert werden können). // // foreach (string detectedDataType in detectedDataTypes) // // { // // //"Account" überspringen, da das vor der foreach schon verarbeitet worden ist. // // if (detectedDataType == "Account") continue; // // Array GremlinTypeArray = GetNewGremlinTypeArray(Gremlin, detectedDataType, numberOfLines); // // Type GremlinType = GetNewGremlinType(Gremlin, detectedDataType).GetType(); // // PropertyInfo[] GremlinTypeProperties = GremlinType.GetProperties(); // // foreach (PropertyInfo property in GremlinTypeProperties) // // { // // if (mappingTable.TryGetValue(property.Name, out int columnNumber)) // // { // // //convert: // // GremlinTypeConverter.Convert(fields[columnNumber]); // // } // // } // // } // //} // //using (GremlinContext db = new()) // //{ // // //add to context // // //savechanges // //} //} //######################################################################################### //Kann gelöscht werden, sobald die generische Funktion zuverlässig funktioniert: //######################################################################################### //private static string RecognizeDataStatic(Dictionary mappingDictionary) //{ // //Logik zur Kategorisierung des Datensatzes: // //Alle verpflichtenden Angaben zu einer Klasse vorhanen? // //Alle vom Importer erwarteten Angaben vorhanden? // //Bei den Enums zusätzlich noch proüfem, dass nur zwei Spalten vorhanden sind, sonst könnten LSAG-Daten falsch identifiziert werden. // // // // // //Products // if (//required by DB: // mappingDictionary.ContainsKey("ProductNumber") // && mappingDictionary.ContainsKey("ListPrice") // //required by importer: // && mappingDictionary.ContainsKey("Weight") // //unique identifier: // && mappingDictionary.ContainsKey("BreakRangeFrom") // ) // { // return "Product"; // } // //LSAG Contact List Tool List // if (//required by DB: // mappingDictionary.ContainsKey("SAPAccountNumber") // && mappingDictionary.ContainsKey("SAPContactNumber") // && mappingDictionary.ContainsKey("AccountTypeCode") // && mappingDictionary.ContainsKey("SubMarketCode") // && mappingDictionary.ContainsKey("LastName") // && mappingDictionary.ContainsKey("AccountName") // //required by importer: // //unique identifier: // && mappingDictionary.ContainsKey("MA_ProductInterests") // ) // { // return "LSAG Contact List Tool List"; // } // //Accounts // if (//required by DB: // mappingDictionary.ContainsKey("AccountName") // && mappingDictionary.ContainsKey("Street") // && mappingDictionary.ContainsKey("ZIP") // && mappingDictionary.ContainsKey("City") // && mappingDictionary.ContainsKey("PhoneNumber") // && mappingDictionary.ContainsKey("SAPAccountNumber") // && mappingDictionary.ContainsKey("AccountCreatedInSAPOn") // && mappingDictionary.ContainsKey("AccountTypeCode") // && mappingDictionary.ContainsKey("SubMarketCode") // //required by importer: // ) // { // return "Account"; // } // //Contacts // if (//required by DB: // mappingDictionary.ContainsKey("LastName") // && mappingDictionary.ContainsKey("SAPContactNumber") // //required by importer: // && mappingDictionary.ContainsKey("FirstName") // && mappingDictionary.ContainsKey("Gender") // ) // { // return "Contact"; // } // //Custom Description // if (//required by DB: // mappingDictionary.ContainsKey("Heading") // //required by importer: // && mappingDictionary.ContainsKey("ProductNumber") // && mappingDictionary.ContainsKey("OptionNumber") // && mappingDictionary.ContainsKey("DescriptionText") // //unique identifier: // && mappingDictionary.ContainsKey("CoverletterText") // ) // { // return "CustomDescription"; // } // //Pseudo-Enums AccountTypes, SubMarkets, ProductLines for DB-initializing // if (mappingDictionary.Count == 2) // { // if (mappingDictionary.ContainsKey("AccountTypeCode") // && mappingDictionary.ContainsKey("AccountTypeDescription") // && mappingDictionary.Count == 2) // { // return "AccountType"; // } // if (mappingDictionary.ContainsKey("SubMarketCode") // && mappingDictionary.ContainsKey("SubMarketDescription") // && mappingDictionary.Count == 2) // { // return "SubMarket"; // } // if (mappingDictionary.ContainsKey("ProductLineCode") // && mappingDictionary.ContainsKey("ProductLineDescription") // && mappingDictionary.Count == 2) // { // return "ProductLine"; // } // } // return "No entity type unambigiously identified!"; //} } }