Archiv der Kategorie: Programmierung

Word VBA: Alle Bilder / OLE-Objekte automatisch verkleinern

Problem

In einem Dokument sollen alle Bilder auf eine fixe Breite von 400 Bildpunkten im Seitenverhältnis verkleinert oder vergrößert werden.

Ansatz

Über das Seitenverhältnis der alten Größe der Seite A (a) zur neuen Größe der Seite A (ax) lässt sich die Aspect Ratio Verhältniszahl errechnen.
Diese Verhältniszahl kann mit der Größe der Seite B (b) des Rechteckes multipliziert werden, um die neue Größe der Seite B (bx) zu ermittlen.

Lösung

Sub bilderKleinerMachen()
    ' By Bjoern Karpenstein
    Dim s As InlineShape
    
    Dim aspectRatio As Double
    
    Dim newWidth As Integer
    
    newWidth = 400
    
    Dim i As Integer
            
    i = 1
        
    For Each s In ActiveDocument.InlineShapes
        s.Select
        If (s.Width > newWidth) Then
            If i > 1 Then
              aspectRatio = CDbl(newWidth) / CDbl(s.Width)
              s.Width = newWidth
              s.Height = CInt(s.Height * aspectRatio)
            End If
            i = i + 1
        End If
    Next
End Sub

VBA (Import Excel in Doors): In Unicode Datei schreiben (TSV Datei erstellen) / Write to unicode file (create TSV file)

Aufgabe – Problem

Der Inhalt eines Excel-Sheets mit Unicode soll in eine Datei geschrieben werden (damit z.B. IBM Doors über File -> Importieren -> Spreadsheet den Inhalt importieren kann).

A unicode file should be written with the contents of an Excel Sheet using VBA

Vorraussetzung – Prerequirement

Im VBA Editor muss im Menü unter „Verweise“ der Punkt „Microsoft Scripting Runtime“ aktiviert werden.
In the VBA Editor a reference should be added „Microsoft Scripting Runtime“

Ansatz – Approach

(Es wird VBA Code im Excel sheet hinterlegt was den Inhalt in eine TSV-Datei, die von Doors importiert werden kann, auslagert).
Das folgende Skript zeigt ein Iterieren über ein komplettes Excel sheet und schreibt die ersten 3 Spalten in Tabulator-Separiert in eine Datei. Somit könnte man eine TSV-Datei generieren die z.B. von IBM Doors eingelesen werden kann.

Lösung – Solution

    Dim letzteZeile As Integer
    Dim i As Integer
    
    Dim ID,Col1,Col2 As String
    
    Dim fso As New FileSystemObject

    ' Declare a TextStream.
    Dim stream As TextStream

    ' Create a TextStream.
    Set stream = fso.CreateTextFile("c:\dev\wingstext.csv", True, True)
      
    ' Finde die letzte Zeile
    letzteZeile = Cells.Find("*", [A1], , , xlByRows, xlPrevious).Row + 1
               
    For i = 1 To letzteZeile
        ID = Tabelle1.Cells(i, 1)
        Col1 = Tabelle1.Cells(i, 2)
        Col2 = Tabelle1.Cells(i, 3)
        
        stream.WriteLine ID & vbTab & Col1 & vbTab & Col2

    Next i
    
    ' Close the file.
    stream.Close

Android&PHP Backend: Netzwerkkommunikation, Authentifizierung und Login

Aufgabenstellung

Es wird eine Möglichkeit gesucht eine Android App mit einen PHP Backend kommunizieren zu lassen

Intention

PHP mit MySQL Server erhält man oft schon mit billigem Webspace. Ein weiterer Vorteil eines fertigen Webpacks/Webspace ist, dass es nicht notwendig wird sich um eine Backupstrategie zu kümmern. In den meisten Fällen (SLAs und AGBs des Providers studieren) liegt dieses Problem in den Händen des Providers. Er kümmert sich auch um die Sicherheit des Servers. Bei Hackerattacken ist man nicht in der Verantwortung den Server zu härten und zu schützen. Daher kann der Provider auch keine Schadensersatzansprüche gegen den Kunden geltend machen, wenn ein Server gekapert wird und z.B. mit der Installation eines Botnetzes die Leistungsfähigkeit des Rechenzentrums beeinträchtigt ist.

Ansatz

  • Implementierung eines möglichst einfachen Authentifierungsmechanismus in PHP mit MySQL-Anbindung
  • Wegkapseln der Serviceaufrufe
  • Asynchrone Verarbeitung der JSON Aufrufe per Java Thread
    • Reaktion in der MainActivity beim Erhalten des Service-Ergebnisses für UI Manipulationen

Der häufigste Ansatz, den man in Tutorials findet, ist die Kommunikation über JSON, einem kompakten Datenformat in für Mensch und Maschine einfach lesbarer Textform zum Zweck des Datenaustauschs zwischen Anwendungen.

Lösung

BjoernServiceCaller.java – Klasse für weggekapselte Service aufrufe mit CallBack-Funktionalität über onLoginResult

package com.caprisoft.contactupload.services;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;

import com.caprisoft.contactupload.MainActivity;

import android.content.Context;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;


public class BjoernsServiceCaller 
{
	
	final String authenticationURL = "authenticationservice.php";
	private static BjoernsServiceCaller instance = null;
	private BjoernsServiceCaller() 	{}
	

    /**
     * Statische Methode, liefert die einzige Instanz dieser
     * Klasse zurück
     */
    public static BjoernsServiceCaller getInstance() 
    {
        if (instance == null) 
        {
            instance = new BjoernsServiceCaller();
        }
        return instance;
    }
	
	
	public void loginUser(final MainActivity ctx, 
                                    final String email, final String password) 
	{
        Thread t = new Thread()
        {
	        public void run() 
	        {
                        //For Preparing Message Pool for the child Thread
		        Looper.prepare(); 
				
		        HttpClient client = new DefaultHttpClient();
                         //Timeout Limit
		        HttpConnectionParams
                               .setConnectionTimeout(client.getParams(), 10000);
		        HttpResponse response;
		        JSONObject json = new JSONObject();
		        try
		        {
		            HttpPost post = new HttpPost(authenticationURL);
		            json.put("email", email);
		            json.put("password", password);
		            StringEntity se = new StringEntity( json.toString());  
		            se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE,
                                          application/json"));
		            post.setEntity(se);
		            response = client.execute(post);
		            /*Checking response */
		            if(response!=null)
		            {
		                BufferedReader reader = new BufferedReader
                                (
                                    new InputStreamReader
                                    (
                                       response.getEntity().getContent(), "UTF-8"
                                    )
                                );
		                String derText = reader.readLine();
                                // CallBack aufruf in MainActivity
		                ctx.onLoginResult(derText);
		            }
		
		        }
		        catch(Exception e)
		        {
		            e.printStackTrace();
		        }
		        Looper.loop(); //Loop in the message queue
	        }
        };
        t.start();          
    }
}

authenticationservice.php – Das PHP Backend, was die Datenbankabfragen macht und auf jedem Billigwebspace läuft

<?php  
    include("configuration/database.php");
	$json = file_get_contents('php://input');
	$obj = json_decode($json);
	$result2=mysql_query("SELECT id, activationcode FROM users ".
                                      "WHERE email='".$obj->{'email'}."' AND ".
                                      "password='".$obj->{'password'}."'") 
                                       or die(mysql_error());
	
	$user="nouser";
	while ($row = mysql_fetch_array($result2, MYSQL_NUM)) {
	   $user=$row[0];
	   $activationcode=$row[1];
	}	
    
    $posts = array(1);
    
    if($user!="nouser")
    {
    	if($activationcode=="activated")
    	{
    		session_start();
    		session_register("userid");
    		$_SESSION["userid"] =$user;
    		header('Content-type: application/json');
    		echo "success";
    	}
    	else 
    	{
    	   $registrationcode=substr(md5($obj->{'email'}." ".$obj->{'password'}),6);
    	   mysql_query("UPDATE users SET activationcode='".$registrationcode.
           "' WHERE email='".$obj->{'email'}."'") or die(mysql_error());
    	   mail($obj->{'email'},"Registrationcode for contactupload.com",
            "Hello!<br><br>\n You or someone else has registered this email ".
            "address via mobile phone.<br><br>\nThe generated registrationcode is ".
            $registrationcode.".<br><br>\nPlease type this registrationcode in ".
            "the register section of the contact sync app.<br><br> .
            "\nWith best regards,<br>\ncontactupload.com");
    		echo "not activated";
    	}
    }
    else 
    {
        echo "not registered";
    } 
    mysql_close($con);
?>

MainActivity.java – Die Klasse präsentiert den Code der Benutzeroberfläche der Activity (VIEW)

package com.caprisoft.contactupload;

import com.caprisoft.contactupload.services.BjoernsServiceCaller;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
	
	private EditText email = null;
	private EditText passwort = null;
	
	/** Called when the user selects the Send button */
	public void onClickLoginButton(View view) {
	    // Do something in response to button
		Log.d(this.getClass().toString(), "Login Button gedrückt!");
		BjoernsServiceCaller.
                getInstance().
                loginUser
                (
                    this, 
                    email.getText().toString(), 
                    passwort.getText().toString()
                );
	}
	
	/** Called when the user selects the Send button */
	public void onClickRegisterButton(View view) {
	    // Do something in response to button
		Log.d(this.getClass().toString(), "Register Button gedrückt!");
	}	
	
	public void onLoginResult(String text)
	{
		Toast.makeText(this, "HALLO: "+text, Toast.LENGTH_LONG).show();
	}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        email = (EditText) findViewById(R.id.textUsername);
        passwort = (EditText) findViewById(R.id.textPassword);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    
}

Activity_main.xml – Das eigentliche Frontend

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/labelUsername"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/labelUsername"
        android:textAppearance="?android:attr/textAppearanceLarge" />
    
  <EditText android:id="@+id/textUsername"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:inputType="textNoSuggestions"
        android:hint="@string/hintUsername" />
  
   <TextView
        android:id="@+id/labelPassword"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/labelPassword"
        android:textAppearance="?android:attr/textAppearanceLarge" />
      
   <EditText android:id="@+id/textPassword"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:inputType="textPassword"
        android:hint="@string/hintPassword" />

   <Button
       android:id="@+id/buttonLogin"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:onClick="onClickLoginButton"
       android:text="@string/buttonLogin" />

   <Button
       android:id="@+id/buttonRegister"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:onClick="onClickRegisterButton"
       android:text="@string/buttonRegister" />
  
</LinearLayout>

AndroidManifest.xml – Permission für den Zugriff auf das Internet einrichten

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.caprisoft.contactupload"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
     <uses-permission android:name="android.permission.INTERNET" /> 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Adobe Flex 3 / C#.NET FluorineFX: Balkendiagramme, Tortendiagramme und Liniendiagramme

Aufgabenstellung

Auf der Basis einer Datenbank-Abfrage werden Balkendiagramme, Tortendiagramme und Liniendiagramme mit Flex 3 und der Middleware FluorineFX, die auf einem Microsoft IIS Server läuft, erstellt.

Ansatz

FluorineFX lässt sich über die Webseite http://www.fluorinefx.com/download.html herunterladen und installieren.
Es handelt sich hierbei lediglich um eine Web-Anwendung, die auf dem Microsoft IIS Server mit dem Enterprise Manager eingebunden werden muss.

Vorraussetzungen

  • Installation von FluorineFX auf dem Microsoft IIS Server
  • Installation von Adobe Flex
  • Installation einer .NET Umgebung
  • Konfiguration aller Komponenten

Lösung

Beispiel

Code in FluorineFX

Der folgende Code wird in Visual Studion (Express) als Projekttyp Klassenbibliothek (oder FluorineFX Service Projekt) mit der Sprache C# genutzt. Die C# Klassenbibliothek wird im kompilierten Zustand (dll-Datei) in den Order „bin“ im FluorineFX-Verzeichnis (welches als Website in IIS eingebunden wurde) kopiert.

Die SQL-Anweisung sollte aus Performance-Gründen möglichst stark aggregiert werden. Das Beispiel benutzt eine zweispaltige Struktur (ReportVO) und kann beliebig angepasst werden.

C# Code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Data.SqlClient;
using FluorineFx; // Wichtig! FluorineFx.dll Verweis einbinden

namespace ITReportingServices
{
    [RemotingService("ReportingService")]
    public class ReportingService
    {
        public ArrayList getReport()
        {
            ArrayList reports = new ArrayList();

            SqlConnection conn = 
            new SqlConnection(MyConfigurationManager.msSqlServerString);

            try
            {
                conn.Open();
                SqlCommand comm = new SqlCommand();
                comm.Connection = conn;
                comm.CommandText = "SELECT MONTH(buchdatum) As Monat, "+
               "sum(wert) AS Summe FROM costcenteraccounting "+
               "WHERE (YEAR(buchdatum)) = 2011 "+
               "AND costcenter='101008960' "+
               "GROUP BY (MONTH(buchdatum)), (YEAR(buchdatum))	";
                SqlDataReader reader = comm.ExecuteReader();
                while (reader.Read())
                {
                    ReportVO vo = new ReportVO();
                    vo.monat = reader.GetValue(0).ToString();
                    vo.summe = reader.GetValue(1).ToString().Replace(",",".");
                    reports.Add(vo);
                }
            }
            catch (Exception e)
            {
                reports = new ArrayList();
                ReportVO vo = new ReportVO();
                vo.monat = e.Message;
                vo.summe = e.Message;
                reports.Add(vo);
                return reports;
            }
            finally
            {
                conn.Close();
            }

            return reports;
        }
    }

    public class ReportVO
    {
        public string monat;
        public string summe;
    }
}

Code in Adobe Flex

Das Model wird auf den .NET-Namespace gemappt.

Model:

package models
{
   [RemoteClass(alias="ITReportingServices.ReportVO")]
   public class ReportVO
   {
       public var monat:String;
       public var summe:String;		
		
       public function ReportVO()
       {
       }
   }
}

Service in Flex-Code aufrufen:

<!-- unter den Script-Teil in MXML schreiben -->
<mx:RemoteObject id="reportingService" 
                             destination="GenericDestination"  
                             source="ITReportingServices.ReportingService" 
                             showBusyCursor="true" 
                             fault="faultHandler(event)" >
       <mx:method name="getReport" result="getReportHandler(event)"/>
</mx:RemoteObject>	
...

// Das hier in mx:Script Teil
[Bindable] public var reports:ArrayCollection=new ArrayCollection();

public function creationComplete():void
{
    reportingService.getReport();
}
			
public function getReportHandler(event:ResultEvent):void
{
     reports = event.result as ArrayCollection;
}
			
public function faultHandler(event:FaultEvent):void
{
   Alert.show(event.fault.toString());
}

Diagramme

Balkendiagramm:

<mx:ColumnChart dataProvider="{reports}" 
                     height="100%" width="100%" id="columnchart1">
    <mx:horizontalAxis>
      <mx:CategoryAxis id="ca" categoryField="monat" title="Market sizes" />
     </mx:horizontalAxis>
     <!-- horizontal axis renderer -->
     <mx:horizontalAxisRenderers>
    	<mx:AxisRenderer axis="{ca}" canDropLabels="false" />
      </mx:horizontalAxisRenderers>
      <mx:series>
            <mx:ColumnSeries displayName="Amount" 
        		xField="monat"
        		yField="summe"/>
       </mx:series>
</mx:ColumnChart>
<mx:Legend visible="false" dataProvider="{columnchart1}"/>		

Tortendiagramm:

<mx:Label text="Linke Seite" />
<mx:PieChart id="pie2" 
   width="100%" height="100%"
   dataaProvider="{reports}"  showDataTips="true">
      <mx:series> 
        <!--Apply the Array of radii to the PieSeries.-->
	<mx:PieSeries field="summe"
                	        nameField="monat"
                	       labelPosition="callout"/>
      </mx:series>
</mx:PieChart>
  <mx:Legend dataProvider="{pie2}"/>

Liniendiagramm:

<mx:LineChart width="100%" height="100%" 
	id="myChart" 
	dataProvider="{reports}" 
        	showDataTips="true">
   <mx:horizontalAxis>
     <mx:CategoryAxis dataProvider="{reports}" 
                	     categoryField="month"/>
    </mx:horizontalAxis>
    <mx:series>
       <mx:LineSeries yField="summe" 
                 	  displayName="Summe"/>       
    </mx:series>
</mx:LineChart>

C#.NET und SQL Server: Transaktionen verwenden

Aufgabenstellung

Mehrere SQL Statements sollen atomar (ACID) ausgeführt werden.

Beispiel:
Ein Datenimport mit mehreren INSERT Statements und vorherigen DELETE auf eine existierende Tabelle: Während ein anderer Benutzer auf die Tabelle zugreift, sieht er nur die Daten die bereits importiert wurden. Dies kann über eine Transaktion atomar (wie das umlegen eines Schalters mit COMMIT) ausgeführt werden.

Manipulationen (z.B. auf ein Konto) mit mehreren Zugriffen unterschiedlicher User (konkurrierende Zugriffe) können zu falschen Beträgen auf dem Konto führen (Nebenläufigkeitsprobleme):
Bei mehreren SQL Kommandos, die sequenziell/hintereinander ausgeführt werden, soll sichergestellt werden das andere Benutzer keine Werte manipulieren, die zu einer falschen Weiterberechnung führen (siehe auch: Lost update und Dirty Read bei Datenbanken).

Vorgehensweise

Es existiert eine Klasse SqlTransaction, deren Instanz über die FactoryMethode .BeginTransaction(„SampleTransaction“); zugewiesen werden kann. Die Instanz kann anschließend einem Command zugwiesen werden, welches bis zum COMMIT (Schalter umlegen/alles übernehmen) Befehle an den SQL Server sendet.

Lösung

private static void ExecuteSqlTransaction(string connectionString)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();

        SqlCommand command = connection.CreateCommand();
        SqlTransaction transaction;

        // Start a local transaction.
        transaction = connection.BeginTransaction("SampleTransaction");

        // Must assign both transaction object and connection
        // to Command object for a pending local transaction
        command.Connection = connection;
        command.Transaction = transaction;

        try
        {
            command.CommandText =
                "Insert into Region (RegionID, RegionDescription) "+
                "VALUES (100, 'Description')";
            command.ExecuteNonQuery();
            command.CommandText =
                "Insert into Region (RegionID, RegionDescription) "+
                 "VALUES (101, 'Description')";
            command.ExecuteNonQuery();

            // Attempt to commit the transaction.
            transaction.Commit();
            Console.WriteLine("Both records are written to database.");
        }
        catch (Exception ex)
        {
            Console.WriteLine("Commit Exception Type: {0}", ex.GetType());
            Console.WriteLine("  Message: {0}", ex.Message);

            // Attempt to roll back the transaction.
            try
            {
                transaction.Rollback();
            }
            catch (Exception ex2)
            {
                // This catch block will handle any errors that may have occurred
                // on the server that would cause the rollback to fail, such as
                // a closed connection.
                Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
                Console.WriteLine("  Message: {0}", ex2.Message);
            }
        }
    }

Adobe Flex 3: mx:DataGrid in die Zwischenablage / Clipboard kopieren (z.B. für Excel)

Aufgabenstellung

Der Inhalt eines mx:DataGrid soll in die Zwischenablage kopiert werden, um ihn z.B. in Excel einzufügen.

Anmerkung: Für AdvancedDataGrid mit Baum siehe auch

Ansatz

Verwendung von System.setClipboard(…); und Iteration über den DataProvider.

Lösung

public function clipData( dg:DataGrid ):void
{
var totalExport:String = new String();
var colList:Array = new Array();
for(var i:int = 0; i < dg.columnCount; i++) { colList.push(dg.columns[i].dataField); totalExport += dg.columns[i].headerText + "\t"; } totalExport += "\r"; for(var yp:int = 0; yp < dg.dataProvider.length; yp++) { for(var xp:int = 0; xp < colList.length; xp++) { totalExport += dg.dataProvider[yp][colList[xp]] + "\t"; } totalExport += "\r"; } System.setClipboard(totalExport); Alert.show('Copied to clipboard, you can paste it now to excel...'); } [/javascript]

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();
    }
}