2017-08-22 2 views
2

Ich habe vor kurzem mit dem Schreiben einer Software beauftragt, Excel-Dateien zu importieren.Importieren von verschiedenen Dateien aus Excel mit verschiedenen Regeln

Das Problem, das ich zu lösen versuche, ist, dass meine Firma c100-Clients hat und jede eine Datei in einem anderen Layout liefern, in dem die Spalten in einer Datei zwischen Clients unterscheiden, aber die relevanten Informationen in jeder Datei vorhanden sind .

Dieser Prozess ist kompliziert aufgrund der Tatsache, dass bestimmte Vorgänge zu verschiedenen Dateien durchgeführt werden müssen.

In einer Datei muss z. B. eine Spalte nach einer specifc-Spalte eingefügt werden, und dann muss das Ergebnis einer Berechnung in diese Spalte eingefügt werden. In diesem Blatt wird eine Adresse über 9 Spalten verteilt, diese Adresse muss in die letzten 6 der 9 Spalten verschoben werden und dann müssen die ersten 3 Spalten entfernt werden.

Was ich nicht tun will, ist die Verarbeitungslogik für jede Datei schreiben (c 100 wie erwähnt) und damit gefangen in den Drang, diesen Code zu pflegen und verantwortlich für das Hinzufügen neuer Kundendateien, wie sie kommen in.

Was ich tun möchte, ist eine Regel oder Verarbeitung Engine der Art zu erstellen, wobei ich grundlegende Regeln wie "Spalte einfügen", "Spalte entfernen", "Berechnung einfügen", "a, b, c, d, e & f Zu verwendende Spalten d, e & f "- Der Grund dafür ist, dass das Konfigurieren des Lesens und Verarbeitens jeder neuen Datei über eine Front-End-Software durch einen Endbenutzer (offensichtlich mit etwas Training) erfolgen kann was ist zu tun).

Gibt es ein Muster oder eine Strategie, die dazu passen? Ich habe über Rules-Engines gelesen, aber die besten Beispiele hierfür sind einfache boolesche Vergleiche wie "Alter = 15" oder "Nachname = 'Smith", aber ich kann kein anständiges Beispiel dafür finden, wie "Spalte nach Spalte G einfügen" "Setzen Sie G - 125 in Spalte H".

Jede Hilfe hier, oder ein Zeiger auf eine gute Vorgehensweise, würde sehr geschätzt werden.

+0

Wenn Sie keine Muster identifizieren können, die in den Dateien wiederholt werden können, sollten Sie die Formatierung manuell durchführen. Gibt es eine Möglichkeit, allen Kunden eine einheitliche Vorlage zur Verfügung zu stellen? Versuchen Sie, die Daten aus Excel zu importieren? Nicht sicher, warum das C# -Tag aufgeführt ist.Es ist ziemlich einfach, Code zu schreiben, um Spalten einzufügen und Formeln in einer Tabelle zu ändern, aber es wird viel schwieriger (und sinnlos), wenn Sie eine vollständig dynamische Reihe von Änderungen benötigen. Vielleicht verstehe ich nicht genau, was Sie zu tun versuchen. –

+0

Ich weiß nicht, ob dies die Antwort ist, nach der Sie suchen, oder welche Beschränkungen Sie haben, aber was wir normalerweise versuchen würden, ist die Standardisierung der Eingabe, indem wir eine Standardvorlage für die Kunden bereitstellen. – ainwood

Antwort

1

Lassen Sie mich sehen, ob ich Ihnen hier helfen kann.

Korrigieren Sie mich, wenn ich falsch liege, aber es scheint, als ob alle Ihre Eingabe- und Ausgabedateien Daten nur in Spalten und Spalten enthalten.

In diesem Fall sollten Sie sich Ihr Problem als Transformation von X-Eingabespalten in Y-Ausgabespalten vorstellen. Für jeden Client benötigen Sie eine Konfiguration, die die Transformation angibt. Die Konfiguration könnte wie folgt aussehen:

Y1 = X1 
Y2 = X1 + X2 
Y3 = X3 + " some string" 

Wie Sie sehen können, sind Ihre Konfigurationszeilen einfach C# -Ausdrücke. Sie können die LINQ Expression class verwenden, um einen Ausdrucksbaum aus Ihren Transformationsformeln zu erstellen. Sie können über Expressions here lernen. Diese Ausdrücke können dann kompiliert und verwendet werden, um die tatsächliche Transformation durchzuführen. Wenn Sie in C# denken, erstellen Sie eine statische Transformationsmethode, die eine Liste als Eingabe verwendet und eine Liste als Ausgabe für jeden Client zurückgibt. Wenn Sie Ausdrücke verwenden, müssen Sie die Konfigurationsdateien selbst analysieren.

Sie können auch die Roslyn Compiler Services verwenden, die die korrekte C# -Syntax unterstützen kann. Auf diese Weise können Sie buchstäblich eine statische Methode verwenden, die die Transformation durchführen kann. Dies entlastet Sie auch von den Parsing-Aufgaben.

In beiden Fällen müssen Sie sich noch mit folgenden Dingen befassen: Soll ich erwarten, dass die Spalten eine Zeichenfolge sind (was bedeutet, dass Ihr Support die Konfigurations-GUI explizit anweisen muss, benötigte Spalten in Zahlen zu analysieren) oder sollte wandelt automatisch nummernähnliche Felder in Zahlen um (jetzt muss die Unterstützung keine zusätzliche Konfiguration vornehmen, aber sie können Probleme bekommen, wenn sie mit Spalten arbeiten, die Nummern wie ID haben, aber als String behandelt werden sollten, um jede unsachgemäße Behandlung zu vermeiden, usw.) .

Zusammengefasst ist mein Ansatz:

  • pro Client-Konfigurationsdatei erstellen.
  • Konvertieren Sie die Konfigurationsdatei in C# Methode Ausdrücke oder Roslyn dynamisch zur Erzeugung dieser Konfiguration
  • Geben Sie eine GUI - auf diese Weise die Unterstützung Person verwandeln können festlegen, die leicht ohne Ihre spezielle Syntax (Expressions) oder C# Syntax (Roslyn) zu wissen, . Beim Speichern der Konfiguration können Sie eine Methode pro Client in einer einzelnen Assembly (oder einer separaten Assembly pro Client) generieren und beibehalten. Nennen wir es Client-Bibliothek.
  • Ihre Hauptanwendung kann alle Standard-Sachen des Lesens von Excel, Validieren, usw. tun und rufen Sie dann die Client-Bibliothek-Methode auf, um die Ausgabe in einem Standardformat zu generieren, das in Ihrer Hauptanwendung weiterverarbeitet werden kann.

Ich hoffe, Sie haben das Wesentliche.

Bearbeiten: Hinzufügen von etwas Code zu demonstrieren. Der Code ist ein wenig langatmig, aber zum Verständnis kommentiert.

// this data represents your excel data 
var data = new string[][] { 
    new string [] { "col_1_1", "10", "09:30" }, 
    new string [] { "col_2_1", "12", "09:40" } 
}; 

// you should read this from your client specific config file/section 
// Remember: you should provide a GUI tool to build this config 
var config = @" 
      output.Add(input[0]); 

      int hours = int.Parse(input[1]); 
      DateTime date = DateTime.Parse(input[2]); 
      date = date.AddHours(hours); 
      output.Add(""Custom Text: "" + date); 
"; 

// this template code should be picked up from a 
// non client specific config file/section 
var code = @" 
using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace ClientLibrary { 
    static class ClientLibrary { 
     public static List<string> Client1(string[] input) { 
      var output = new List<string>(); 

      <<code-from-config>> 

      return output; 
     } 
    } 
} 
"; 

// Inject client configuration into template to form full code 
code = code.Replace(@"<<code-from-config>>", config); 

// Compile your dynamic method and get a reference to it 
var references = new MetadataReference[] { 
    MetadataReference.CreateFromFile(typeof(object).Assembly.Location), 
    MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location) 
}; 

CSharpCompilation compilation = CSharpCompilation.Create(
    null, 
    syntaxTrees: new[] { CSharpSyntaxTree.ParseText(code) }, 
    references: references, 
    options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); 


MethodInfo clientMethod = null; 
using (var ms = new MemoryStream()) { 
    EmitResult result = compilation.Emit(ms); 

    if (!result.Success) { 
     foreach (Diagnostic diagnostic in result.Diagnostics) { 
      Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage()); 
     } 
    } else { 
     ms.Seek(0, SeekOrigin.Begin); 
     Assembly assembly = Assembly.Load(ms.ToArray()); 
     clientMethod = assembly.GetType("ClientLibrary.ClientLibrary").GetMethod("Client1"); 
    } 
} 

if (clientMethod == null) 
    return; 

// Do transformation 
foreach (string[] row in data) { 
    var output = clientMethod.Invoke(null, new object[] { row }) as List<string>; 
    Console.WriteLine(string.Join("|", output)); 
} 

Sie werden einige nuget Bibliotheken müssen diese kompilieren, und ihre entsprechende Verwendung von Klauseln

nuget install Microsoft.Net.Compilers # Install C# and VB compilers 
nuget install Microsoft.CodeAnalysis # Install Language APIs and Services 

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Reflection; 
using Microsoft.CodeAnalysis; 
using Microsoft.CodeAnalysis.CSharp; 
using Microsoft.CodeAnalysis.Emit; 

Wie Sie feststellen, das einzige Stück zu befürchten ist die GUI automatisch erzeugen, um den Code für die Transformation - die ich hier nicht zur Verfügung gestellt habe. Wenn Sie einfache Transformationen wünschen, sollte das sehr einfach sein, aber für eine komplexe Transformation wird es mehr involviert sein.

-2

Ich würde vorschlagen, dass Sie eine XML-Konfigurationsdatei für jede Excel-Datei pflegen. Die XML-Konfiguration muss von einem Tool gelesen werden, möglicherweise eine Konsolenanwendung, und basierend auf der XML-Konfiguration eine neue CSV-Datei generieren.

Da die XML-Konfigurationsdatei von jedem Texteditor einfach bearbeitet werden kann, können Benutzer dieselben aktualisieren.

0

Es klingt, als ob Sie erwarten, dass Ihr Endbenutzer technisch versiert genug ist, um diesen Konfigurationsmechanismus zu verstehen, den Sie schreiben werden. Wenn sie mit diesen technischen Details umgehen können, ist es möglicherweise einfacher, ihnen ein Excel-Buch und eine offizielle Excel-Vorlage zu geben, die alle Spalten enthält, die Ihre Import-App benötigt, und sie können die Daten manuell in die Spezifikation einarbeiten.

Ansonsten würde ich eine auf Strategiedesign basierende Musterlösung vorschlagen, um eine Bibliothek von "Data-Massager" -Klassen für bekannte Formate zu erstellen, und einfach neue Klassen hinzufügen, wenn neue Formate gefunden werden. z.B.

public interface IClientDataImporter 
{ 
    List<MyCustomRowStructure> Import(string filename); 
} 

// client 1 importer 
public class ClientOneImporter : IClientDataImporter 
{ 
    public List<MyCustomRowStructure> Import(string filename) 
    { 
     var result = new List<MyCustomRowStructure>(); 
     // ..... insert custom logic here 
     return result; 
    } 
} 

// client 2 importer 
public class ClientTwoImporter : IClientDataImporter 
{ 
    public List<MyCustomRowStructure> Import(string filename) 
    { 
     var result = new List<MyCustomRowStructure>(); 
     // ..... insert custom logic here 
     return result; 
    } 
} 

// repeat up to however many formats you need 

// then..... 

public class ExcelToDatabaseImporter 
{ 
    public void ImportExcelFile(string filename, string clientName) 
    { 
     var myValidData = GetClientDataImporter(clientName).Import(filename); 
     StickMyDataToMyDatabase(myValidData); // this is where you would load the structure into the db... won't need to touch every time a new format is encountered 
    } 
    public IClientDataImporter GetClientDataImporter(string clientName) 
    { 
     switch (clientName): 
      case "ClientOne": 
       return new ClientOneImporter(); 
       break; 
      case "ClientTwo": 
       return new ClientTwoImporter(); 
       break; 
      default: 
       throw new ArgumentException("No importer for client"); 
       break; 

    } 
} 
Verwandte Themen