Feedback

C# - Währungskurse in Datenbank speichern

Veröffentlicht von am 23.11.2008
(1 Bewertungen)
Hallo :-)

Unter http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html#dowloads
sind Währungskurse von 1999 bis heute als XML-Dateien verfügbar.

Mit dieser Methode könnt Ihr diese Daten auslesen und in eine Datenbank speichern.

Die Tabelle der Datenbank enthält drei Spalten:
Datum (Datetime),
Waehrung (nchar(3)),
Kurs (decimal(18,4))

Ganz unten findet Ihr ein Beispiel wie diese Methode aufgerufen werden könnte.
//using System;
//using System.Data;
//using System.Data.SqlClient;
//using System.Net;
//using System.Xml;

private void WaehrungsKursInDatenbankSpeichern(String urlDerXML, String zielTabelle, SqlConnection con)
{
    XmlTextReader xmlReader;

    DataTable dtWechselkurs = new DataTable();
    dtWechselkurs.Columns.Add(new DataColumn("Datum",System.Type.GetType("System.DateTime")));
    dtWechselkurs.Columns.Add(new DataColumn("Waehrung", System.Type.GetType("System.String")));
    dtWechselkurs.Columns.Add(new DataColumn("Kurs", System.Type.GetType("System.Decimal")));

    try
    {
        //XML-Daten übers Internet einlesen
        xmlReader = new XmlTextReader(urlDerXML);
    }
    catch( WebException )
    {
        throw new WebException("Währungsdaten konnten nicht abgerufen werden!");
    }

    try
    {
        DateTime tempDatum = DateTime.Now;
        String tempWaehrung;
        Decimal tempKurs;

        while (xmlReader.Read())
        {
            if (xmlReader.Name != "")
            {

                for (int i = 0; i < xmlReader.AttributeCount; i++)
                {
                    //Prüfen ob es den Knoten/Element 'Cube' gibt
                    if (xmlReader.Name == "Cube")
                    {
                        //Falls der Knoten/Element nur 1 Attribut enthält, ist dies das Datum
                        if (xmlReader.AttributeCount == 1)
                        {
                            //Datum auslesen
                            xmlReader.MoveToAttribute("time");
                            tempDatum = DateTime.Parse(xmlReader.Value);
                        }
                        //Sind 2 Attribute im aktuellen Knoten/UnterElement, enthält dieser WährungsKürzel und Kurswert
                        if (xmlReader.AttributeCount == 2)
                        {
                            //Währung auslesen
                            xmlReader.MoveToAttribute("currency");
                            tempWaehrung = xmlReader.Value;

                            //Kurs auslesen
                            xmlReader.MoveToAttribute("rate");
                            tempKurs = decimal.Parse(xmlReader.Value.Replace(".", ",")); // Komma als DecimalSymbol
                            
                            //ausgelesene Werte zusammen in einer neuen Zeile eintragen
                            DataRow neueZeile = dtWechselkurs.NewRow();
                            neueZeile["Datum"] = tempDatum;
                            neueZeile["Waehrung"] = tempWaehrung;
                            neueZeile["Kurs"] = tempKurs;

                            dtWechselkurs.Rows.Add(neueZeile); 
                        }

                        xmlReader.MoveToNextAttribute();
                    }
                }
            }
        }
        //Verbindung zur Datenbank herstellen
        con.Open();

        //Alle Kurse wurden in dtWechselkurs eingefügt und werden jetzt an die Datenbank geschickt
        SqlBulkCopy bcopy = new SqlBulkCopy(con);
        bcopy.DestinationTableName = zielTabelle;
        bcopy.WriteToServer(dtWechselkurs);
        con.Close();
        MessageBox.Show("Währungskurs erfolgreich eingefügt");
    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message,"Währungskurs konnte nicht importiert werden");
    }
}


//es folgt ein Beispiel zum Aufruf der Methode
private void BeispielAufruf_WährungSpeichern(object sender, EventArgs e)
{
    String urlDerXML = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml";
        //folgende Kursdaten sind verfügbar:
        //Kurse von heute = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"
        //letzten 90 Tage = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml";
        //Kurse seit 1999= "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.xml";


    String zielTabelle = "Wechselkurs";

    //Verbindungsaufbau definieren
        SqlConnectionStringBuilder conBuilder = new SqlConnectionStringBuilder();
        conBuilder.DataSource = "(local)";        //kann auch eine IP oder Name sein, dann ohne Klammern
        conBuilder.InitialCatalog = "knoxyz";
        conBuilder.ConnectTimeout = 10;
        conBuilder.IntegratedSecurity = true; //oder User & Password
        //conBuilder.UserID = "knoxyz";
        //conBuilder.Password = "xyz";
        SqlConnection con = new SqlConnection(conBuilder.ConnectionString);
    
   
    WaehrungsKursInDatenbankSpeichern(urlDerXML,zielTabelle,con);
}

7 Kommentare zum Snippet

knoyxz schrieb am 24.11.2008:
Warum denn diese Bewertung (2,6)?
Falls jemand einen effektiv eren Weg findet,
Währungsdaten in eine Datenbank zu bekommen,
kann er mir dies sehr gern mitteilen.

Für mich ist dieser Schritt wirklich wichtig,
um in einheitlicher Währung, eine Bestellübersicht von offene/abgeschlossene Positionen zu erhalten die international (in verschiedenen Währungen) getätigt wurden.

knoxyz
Günther Foidl schrieb am 24.11.2008:
Einige Anmerkungen zum Code:
1) die Verbindung wird nicht geschlossen
2) die Ressourcen werden nicht freigegeben -> verwende dazu am besten using (...)
3) besser wäre auch im DataTable den Spaltentyp auch anzugeben
4) effizienter ist bei decimal.Parse im String nicht den (.) durch (,) zu ersetzten denn das ist eine String-Operation und die ist aufwändig. Stattdessen die invariante Kultur verwenden, dort ist der (.) das Dezimaltrennzeichen.
decimal kurs = decimal.Parse(string, System.Globalization.NumberForamtInfo.InvariantInfo);

5) funktioniert (bei mir) nicht da beim BulkCopy ein Fehler geworfen wird dass "Der angegebene Wert vom Typ String aus der Datenquelle kann nicht in Typ datetime der angegebenen Zielspalte konvertiert werden."
6) kann mit LINQ eleganter gelöst werden (was effizienter ist weiß ich nicht)

private static void KursXML2DB(string url, KursDataContext db)
{
XElement xml = XElement.Load(url).Element("Cube");
var xmlKurse =
from k1 in xml.Elements()
from k2 in k1.Elements()
select new Kurs
{
Datum = DateTime.Parse(k1.Attribute("time").Value),
Währung = k2.Attribute("currency").Value,
Kurs1 = decimal.Parse(k2.Attribute("rate").Value, NumberFormatInfo.InvariantInfo)
};

db.Kurs.InsertAllOnSubmit(xmlKurse);
db.SubmitChanges();
db.Dispose();
}
knoyxz schrieb am 25.11.2008:
Hallo Günther,

habe ebenfalls einige Hinweise zu deinen Anmerkungen:
1) Die Verbindung wird nie, bzw. nur durch TimeOut, geschlossen. Ein con.Close() ist lediglich der Optik schön. Tatsächlich bleibt die Verbindung jedoch im Connection-Pool weiterhin geöffnet.

2) Ressourcen könnten wirklich freigegeben werden, ist bei der geringen Menge an Daten jedoch nicht nötig

3) DatenTyp im DataTable zu definieren ist tatsächlich Sinnvoll, leider habe ich es auch nach einigen ausprobieren nicht geschafft. Für ein Beispiel währe ich dankbar.

4) Hier liegst du leider daneben, dass Komma muss sein wenn nicht alles mit 1000 multipliziert werden soll! Du rufst die kulturunabhängige Formatierung (entspricht die amerikanische) Formatierung auf.
Richtig ist:
decimal kurs = decimal.Parse(String, System.Globalization.NumberFormatInfo.CurrentInfo);


5) Kann die Fehlermeldung leider nicht reproduzieren. Hast du dein System evtl. auf eine englische Länderkennung eingestellt?

6) In unserem Unternehen ist bisher nur .Net 2.0 vorhanden und so LINQ nicht verfügbar.

PS: Danke für deine begründete Bewertung ;-)
knoxyz
knoyxz schrieb am 25.11.2008:
Der Punkt 4, Datentyp für Spalten im DataTable festlegen, ist nun im Code enthalten.
Günther Foidl schrieb am 25.11.2008:
1) Connection: Genau aus diesem Grund sollte die Verbindung wieder in den Pool mit Close() zurückgelegt werden. Sonst könnte es passieren dass der Pool erschöpft wird und somit neue Verbindungen geöffnet werden müssen.
2) Mit der InvariantInfo liege ich genau richtig. Die Daten haben als Dezimaltrennzeichen einen (.). Dieser wird bei der Invarianten Kultur - wie du richtig angemerkt hast - auch verwendet und somit stimmt das Parsen. Bei der von dir vorgeschlagenen CurrentInfo wäre das auf einem deutschsprachigen System ein (,) und das Ergebnis wäre um den Faktor 10E3 falsch. Probiers und du wirst es sehen dass ich richtig liege.
3) Fehler: ich hab Deutsch/Österreich - ist aber nebensächlich
4) Ich hab noch gar nicht angemerkt dass mir das Snippet sonst gut gefällt!

mfG Günther
knoyxz schrieb am 25.11.2008:
Hi

ok, mit dem 'CurrentInfo' lag ich jetzt wirklich falsch!
Hab eben dein 'InvariantInfo' und das 'replace' ausprobiert, funktioniert beides.

Die Ursache für deine Fehlermeldung konnte ich nun wahrscheinlich ausfindig machen.
Liegt sicher daran, dass du eine andere Spaltenreichenfolge in der Tabelle hast, oder?
Sollte, wie im DataTable, erst 'Datum', 'Währung' und dann 'Kurs' sein.

Schöne Grüße
knoxyz
Günther Foidl schrieb am 25.11.2008:
Meine Tabelle ist:

CREATE TABLE Wechselkurs
(
Datum datetime,
Waehrung nvarchar(10),
Kurs money
);

Ich prüfe das nochmal.

mfG Günther
 

Logge dich ein, um hier zu kommentieren!