Alle Beiträge von Björn Karpenstein

Diplom Informatiker, Programmierer, Musikbegeisterter

C#.NET: Eine Datei zeilenweise einlesen

Aufgabenstellung

Eine Datei wird geöffnet und mit einem Dateizeiger über diese iteriert. Das Einlesen von Strukturdateien (z.B. CSV-Dateien) soll somit ermöglicht werden.

Ansatz

Verwendung der Klasse StreamReader zum Öffnen der Datei und der Methoden ReadLine() um Zeile-für-Zeile einen String zu erhalten.

Lösung


// Pfad muss backslashes maskieren (doppelt)
string pfadZurDatei=
"C:\\meinOrdner\\datei.txt"; 

if (!File.Exists(pfadZurDatei))
{
    // Alternativ Console.Write() nutzen
    MessageBox.Show(
   "Die Datei existiert nicht! Bitte Datei wählen!",
    "Wots lous?");
}
else
{
    StreamReader sr = new StreamReader(pfadZurDatei);
    string inputLine = "";
    
    // Zeilenzahl (um beim Einlesen z.B. Zeilen zu skippen)
    int i=0;
    while ((inputLine = sr.ReadLine()) != null)
    {
        i++;
       // Zeile steht in inputLine... 
       // Es handelt sich um eine CSV Datei
       values = inputLine.Split(new Char[] { ';' });

       // Iteriere über jede Zelle der Zeile
       for(int j = 0; j < values.length; j++)
       {
          string eineZelle=values[j];
       }
    }
}

MS SQL Server und C#.NET: Textdatei per SQL Bulk Insert

Aufgabenstellung

Es wird eine perfomante (ohne Cursor-Iterationen) Möglichkeit gesucht um Textdateien per C#.NET in den Microsoft SQL Server zu importieren.

Ansatz

C# bietet eine Klassenbibliothek mit den Klassen SqlBulkCopy und SqlBulkCopyOptions, die bereits im SAP Stammdaten Importer Tool vorgestellt wurde, und dessen Quellcode im unteren Abschnitt des Artikel als Visual Studio 2005 Projekt zum download angeboten wird. Dieses Tool ermöglicht das einlesen von SE16-Downloaddateien mit ein paar Millionen Datensätzen in weniger als 2 Sekunden. Bei der herkömmlichen Methode, einer Cursor-Iteration, wird je INSERT-Statement ein Datenbankaufruf/Netzwerkzugriff durchgeführt. Dies ist nur nicht mehr notwendig.

Lösung

DataTable füllen


...
DataTable dataTable = new DataTable();
...
// Spaltenüberschriften zum Table hinzufügen (in einer Schleife befüllbar)
for (/*Schleife um die Spaltennamen einzulesen*/)
{
   // Die Anzahl der Columns muss = objects array sein
   dataTable.Columns.Add("SPALTENNAME");
}
...
// Definition des Zeilenarrays
ArrayList objects = new ArrayList();
...
while(/*Schleife um zeilenweise über Datei zu iterieren*/)
{
  ...
  for (/*Schleife um über Zellen der Zeile zu iterieren*/)
  {
     ...
     objects.Add(aktuellesZellenElementString);
     ...
  }
   ...
   // Hinzufügen des Datenelements, das die Zeilendaten hat
   dataTable.Rows.Add(objects.ToArray());
}
...


DataTable in den MS SQL Server importieren


private void WriteToDatabase()
{
    // get your connection string
    string connString = msSqlServerString;
    // connect to SQL
    using (SqlConnection connection =  new SqlConnection(connString))
    {
        // make sure to enable triggers
        // more on triggers in next post

        SqlBulkCopy bulkCopy = new SqlBulkCopy
        (
            connection,
            SqlBulkCopyOptions.TableLock |
            SqlBulkCopyOptions.FireTriggers |
            SqlBulkCopyOptions.UseInternalTransaction,
            null
         );

        // set the destination table name
        bulkCopy.DestinationTableName = txtImportTable.Text;
        connection.Open();

        // write the data in the "dataTable"
        try
        {
            bulkCopy.WriteToServer(dataTable);
        }
        catch (Exception e)
        {
            MessageBox.Show("Es ist ein Fehler aufgetreten. "+
            "Stellen Sie sicher dass der Timeout im SQL Server "+
            "auf unendlich steht und AutoClose für die Verbindung "+
            "nicht aktiviert wurde. " + e.Message, "Fehler!");
        }
        connection.Close();
    }
    // reset
    this.dataTable.Clear();
}

Ein komplettes Beispiel lässt sich hier downloaden:
Quellcode SAP Stammdaten Importer (VS 2005 Projekt)

C#.NET: LDAP Zugriff

Aufgabenstellung

In größeren Firmen kann über das LDAP-Protkoll auf Personendaten und -informationen zugegriffen werden. Die kann z.B. für eine Authentifizierung per Singlesignon genutzt werden.

Ansatz

Möchte man diesen Dienst nutzen, muss man zunächst wissen auf welchem Server im Unternehmen ein solcher Dienst angeboten wird. Dies könnte z.B. ein Active Directory Server (Windows Server) oder ein Lotus Notes Server (notesldap) sein.

Zunächst benötigt man den Import:

using System.DirectoryServices;

Lösung

private void button1_Click(object sender, EventArgs e)
{
    DirectoryEntry DirEntry = new DirectoryEntry();
    {
        DirEntry.Path = "LDAP://notesldap.it.company.com";
        DirEntry.AuthenticationType = AuthenticationTypes.ServerBind;
    }
    Console.WriteLine(DirEntry.Name);

    DirectorySearcher search = new DirectorySearcher(DirEntry);
    {
        // Tweak this to refine your search
        search.Filter = "(sn=Karpenstein)";
        // I limit my results while I tweak          
        search.SearchScope = SearchScope.Subtree;
    }
    try
    {
        foreach (SearchResult result in search.FindAll())
        {
            if (result != null)
            {
                DirectoryEntry de = result.GetDirectoryEntry();
                textBox1.Text += "===================================="+br();
                textBox1.Text +=
                "User: \t\t" + de.Properties["uid"].Value.ToString() + br() +
                "objectclass:\t\t" + de.Properties["objectclass"].Value.ToString();

                IDictionaryEnumerator ide = de.Properties.GetEnumerator();
                ide.Reset();

                try
                {
                    while (ide.MoveNext())
                    {
                        PropertyValueCollection pvc = ide.Entry.Value as 
                                               PropertyValueCollection;

                        textBox1.Text += ide.Entry.Key.ToString();
                        textBox1.Text += pvc.Value;
                    }
                }
                catch (Exception sdf)
                {
                    textBox1.Text += sdf.Message;
                }
                
                //textBox1.Text += "location:\t\t" + de.Properties["location"].
                                              Value.ToString() + br();
                //textBox1.Text += "maildomain:\t\t" + de.Properties["maildomain"].
                                              Value.ToString() + br();

                textBox1.Text += "sn:\t\t" + de.Properties["sn"].
                                            Value.ToString() + br();
                //textBox1.Text += "dominocertificate:\t\t" + 
                    de.Properties["dominocertificate"].
                                             Value.ToString() + br();
                textBox1.Text += "mail:\t\t" + de.Properties["mail"].
                                             Value.ToString() + br();
                textBox1.Text += "l:\t\t" + de.Properties["l"].
                                            Value.ToString() + br();
                textBox1.Text += "displayname:\t\t" + de.Properties["displayname"].
                                            Value.ToString() + br();
                textBox1.Text += "givenname:\t\t" + de.Properties["givenname"].
                                           Value.ToString() + br();
                //textBox1.Text += "dc:\t\t" + de.Properties["dc"].
                                           Value.ToString() + br();
                //textBox1.Text += "dn:\t\t" + de.Properties["dn"].
                                           Value.ToString() + br();
                textBox1.Text += "cn:\t\t" + de.Properties["cn"].
                                            Value.ToString() + br();
                textBox1.Text += "====================================";
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message.ToString());
    }
    finally
    {
        search.Dispose();
        DirEntry.Close();
    }
}

SAP Enterprise Portal: Deeplinks zu einer Portalseite

Problem

Möchte man eine Seite mit einem iView aufrufen, ist es meistens nötig die komplette Navigation bis zu diesem durchzuklicken. Das Portal besteht bei der Verwendung von iViews aus Frames.

Ansatz

Möchte man das SAP Enterprise Portal anweisen ein spezielles iView/eine spezielle Seite über einen Deeplink in den Frame zu laden, kann der folgende Trick genutzt werden.

Lösung

  1. Zunächst geht man auf die Portalseite, für die ein Deeplink benötight wird
  2. Zu die Adressleiste vom Browser gibt man javascript:void(prompt(„“,nodeid)); ein
  3. Der Deeplink setzt sich folgendermaßen zusammen: http://<hostname>:<port>/irj/portal?NavigationTarget=<javascript-result>

iView-Entwickler können den folgenden Code benutzen um Parameter über diesen Deeplink zu übergeben:

URL: http://firmenportalrechner.com:50000/irj/portal?NavigationTarget=navurl://885a76f8628af28c18d76773f98eaaaf&materialId=9380027

Java (z.B. iView): String materialId = (String) getPortalRequest().getServletRequest().getParameter(„materialId“);

SQL Server: Anzahl Nachkommastellen ermitteln

Aufgabe

Es sollen die Anzahl der Nachkommastelle in einem SQL Query abgefragt werden

Lösung

SELECT * FROM completeyear2011ecoflac 
WHERE 
right(cast(completeactualyear as varchar(32)), 
  len(cast(completeactualyear as varchar(32))) 
 - charindex('.',cast(completeactualyear as varchar(32))))>0

Beurteilung

Wenn die SQL-Umgebung keine Funktion zur Verfügung stellt, kann man die obige SQL zwar nutzen, sollte den hinteren Teil allerdings in eine Funktion auslagern. Das Auslagern in eine Funktion dient der Übersichtlichkeit und verletzt das DRY-Prinzip nicht (Don’t repeat yourself!)

VBA: Einsatz und Ersatz für den trinären Operator ?:

Aufgabenstellung

In Hochsprachen wie C#.NET und JAVA hilft der Einsatz vom trinären Operator oftmals, den Code leserlicher zu machen.

Ein Beispiel dafür ist das Zuweisen von Werten für ein Datenbankmodell bei einer Cursor-Iteration.

Wenn eine Materialnummer nicht vorhanden/leer ist, soll als String „not available“ angegeben werden:

Bsp. in C#

...
SqlDataReader reader = comm.ExecuteReader();
...
while (reader.Read())
{
   vo = new WorkflowPositionVO();
   vo.material = (reader.GetValue(0).ToString()=="")?
                       "not available":reader.GetValue(0).ToString();
...

Da der obige Code kompakt auf einer Zeile steht, und die gewünschte Funktionalität nicht durch 6-zeilige if/else-Konstrukte gewährleistet wird, führt dies bei vielen Attributen des Objektes „WorkflowPositionVO“ zu einer erhöhten Lesbarkeit des Codes. Man stelle sich 100 WorkflowPositionVO-Attribute vor, die zugewiesen werden müssen. Die Zahl von 600 (+100 Leerzeilen) würde auf 100 Zeilen reduziert werden, und die Fehleranfälligkeit dramatisch reduziert.

Problem

Der trinäre Operator ?: wird in VBA leider nicht unterstützt.

Ansatz

Die Funktionalität von ?: kann auf einen einfachen IF/ELSE-Zweig abgebildet werden, den man in eine Funktion auslagern könnte. Man könnte sich jetzt eine Funktion selbst implementieren, oder die fertige Funktion IIF benutzen. Hierbei handelt es sich um eine echte VBA-Funktion, die evtl. bereits durch ACCESS-Abfragen in SQL Statements bekannt ist.

Lösung

Bsp. in VBA:

   material = IIf(Tabelle.Cells(i,1)="","not available",Tabelle.Cells(i,1))

C#.NET: EMails über SMTP Server versenden

Aufgabenstellung

Ein C#.NET Programm soll eine eMail über einen SMTP Server versenden.

Ansatz

Durch Verwendung der Klasse SmtpClient lassen sich eMails folgendermaßen versenden…

Lösung

public string sendMail(string empfaenger, string betreff, string text)
{
  try
  {
     SmtpClient client = new SmtpClient("smtpserverip");
     MailAddress adr_from = new MailAddress("noreply@firma.com", "Absender");
     MailAddress adr_to = new MailAddress(empfaenger);
     MailMessage message = new MailMessage(adr_from, adr_to);
     message.Body = text;
     message.Subject = betreff;
     message.IsBodyHtml = true;
     message.Priority = MailPriority.Normal;

     client.Send(message);
     client = null;
  }
  catch (Exception ex)
  {
     return ex.Message;
  }

  return "ok";
}

C# .NET + Access : Auf .mdb-Datei zugreifen

Aufgabenstellung

Von C# soll auf eine Access-Datenbank zugegrieffen werden.

Lösung

public static string getCustomerCPD(string customerno)
{
   string cpd = "Customer using getCustomerCPD not found";
   OleDbConnection conn = new OleDbConnection(
   "Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Registry Path=;"+
    "Jet OLEDB:Database Locking Mode=1; "+
    "Data Source=\"" + AppDomain.CurrentDomain.BaseDirectory +
     "\\OCT.mdb\";Jet OLEDB:Engine Type=5;"+
    "Provider=\"Microsoft.Jet.OLEDB.4.0\";Jet OLEDB:System database=;"+
    "Jet OLEDB:SFP=False;"+
    "persist security info=False;Extended Properties=;"+
    "Jet OLEDB:Encrypt Database=False;"+
    "Jet OLEDB:Create System Database=False;"+
     "Jet OLEDB:Don't Copy Locale on Compact=False;"+
    "Jet OLEDB:Compact Without Replica Repair=False;User ID=Admin;"+
     "Jet OLEDB:Global Bulk Transactions=1";);

     try
     {
       conn.Open();
       OleDbCommand comm = new OleDbCommand();
       comm.Connection = conn;

        comm.CommandText = "SELECT cpdnumber FROM [tbl_Customer] "+
                                           "WHERE debitor=@customerno";
        comm.Parameters.AddWithValue("customerno", customerno);

        OleDbDataReader reader = comm.ExecuteReader();

        while (reader.Read())
        {
            cpd = reader.GetValue(0).ToString();
        }
  }
  catch (Exception e)
  {
     return e.Message;
  }
  finally
  {
      conn.Close();
  }
  return cpd;
}

VBA: Dictionary / HashMap / assoziatives Array erstellen

Aufgabenstellung

Wenn es nicht möglich ist, Werte nach einem fortlaufendem Index (Ordinalskala) zu klassifizieren, da keine wirklich logische Reihenfolge existiert, kann man auf assoziative Array/HashMaps oder Dictionaries (synonyme Bezeichnung für nominell skalierte Merkmale) zurückgreifen. Dies ist in der Regel aussagekräftiger als ein Index. Diese Datenstruktur bietet beim Zugriff auf ein Element eine sehr hohe Performance. Bei einem Index müßte man alle Elemente der Datenstruktur durchlaufen um einen geeigneten Schlüssel zu finden.

Ansatz

Da VBA nativ keine solche Datenstruktur besítzt, wird auf Microsoft Scripting Runtime Object Library (scrrun.dll) zugegriffen.

Vorraussetzungen

Die DLL Datei hierfür wird automatisch ab Office 2000 mitinstalliert. Im VBA Editor muss unter „Tools -> Verweise/References“ die Microsoft Scripting Runtime option angehakt werden.

Lösung

   Dim dictio As Object
   Dim dictioItem As Variant
    
   Set dictio = CreateObject("scripting.dictionary")
   dictio.Add "key1", "value1"
   dictio("key2") = "value2"
   dictio("key3") = "value3"

   MsgBox "-" & dictio("gibtsnich") & "-"
   Dim i As Integer
   i = 1
   For Each dictioItem In dictio
      If dictio(dictioItem) = "" Then Cells(i, 14).Value = "ich sagte das gibts nich"
      Cells(i, 12).Value = dictioItem
      Cells(i, 13).Value = dictio(dictioItem)
      i = i + 1
   Next

VBA und Access : Datenbankzugriff von VBA auf .mdb-Datei

Aufgabenstellung

Von einer Office/VBA-Anwendung aus, soll auf eine Microsoft Access Datenbank (.mdb-Datei) zugegriffen werden.

Vorraussetzungen

Im VBA Editor im Menüpunkt Verweise wird die Option Microsoft ActiveX ADO Objects angehakt.

Lösung

Selektionsanweisung

Public Sub HoleDaten()
   Dim cn As New ADODB.Connection
   Dim rs As New ADODB.Recordset
   Dim i As Integer

' Datei liegt im aktuellen Projektverzeichnis
   cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & CurrentProject.Path & _
                 "\diedatei.mdb"

   Set rs = cn.Execute("SELECT * FROM einetabelle")

   i = 1
   Do While Not rs.EOF
      Cells(i, 1).Value = rs.Fields("Feld1")
      Cells(i, 2).Value = rs.Fields("Feld2")
      Cells(i, 3).Value = rs.Fields("Feld3")
      Cells(i, 4).Value = rs.Fields("Feld4")
      rs.MoveNext
      i = i + 1
   Loop
   cn.Close
End Sub

Manipulationsanweisung

Public Sub SchubseInMDB()
   Dim cn As New ADODB.Connection
   Dim rs As New ADODB.Recordset

   cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=unfug.mdb"

   Set rs = cn.Execute("INSERT INTO einetabelle(zweck)VALUES ('Unsinn')")
   cn.Close
End Sub