Laden...

MySQL Zugriff von mehreren Programmen über gemeinsame DLL

Erstellt von Quero vor 13 Jahren Letzter Beitrag vor 13 Jahren 1.686 Views
Q
Quero Themenstarter:in
12 Beiträge seit 2009
vor 13 Jahren
MySQL Zugriff von mehreren Programmen über gemeinsame DLL

Der Plot:

Datenbank MySQL 5.0, Zugriff über "MySQL Connector Net 6.1.2"

Visual Studio 2005 Desktop-Anwendung in C#.

.NET Framework 2

Die Anwendung teilt sich auf in ein Hauptprogramm (Windows-Forms) und einen Windows-Service.
Um die Datenbank irgendwann einfach tauschen zu können, geschehen alle db-Zugriffe über eine gemeinsame DLL (auch ein C# .NET Library).

Zu 99,9% funktioniert alles sehr gut, so dass ich Programmierfehler oder Architektur-Probleme auf meiner Seite eigentlich ausschließe.

Nur selten tritt ein Fehler auf beim MySQL Zugriff. Das kann beim Open sein, beim CreateCommand, ExecuteReader - was auch immer. Der Service oder das Hauptprogramm haben dann Probleme (nie beide gleichzeitig).

Der Fehler ist "Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt." Nein, hier handelt es sich wohl nicht um einen Anfängerfehler beim Zugriff auf Null-Objekte.

Das ganze erinnert eher an: http://support.microsoft.com/kb/810098/de
Mit der dort angeführten Lösung kann ich allerdings nichts anfangen. Erstens gilt die für Framework 1, zweitens werden doch meine Zugriffe nicht mit einer "adodb.dll" erledigt, sondern mit der "MySql.Data.dll" - wenn ich das so richtig sehe.

Jeglicher Ratschlag ist willkommen.

S
324 Beiträge seit 2007
vor 13 Jahren

Passiert der zugriff über LAN oder Internet (verbindungsprobleme)?
Machst du übermäßig viele Abfragen (Problem mit gleichzeitig offenen Connections) ?

Hast du schön Using-Blöcke und try/catch verwendet?
Man sieht leider nichts von deinem Code für den zugriff 😕

1.457 Beiträge seit 2004
vor 13 Jahren

Hallo Quero,

Bist du dir sicher das der Fehler beim Zugriff auf der Datenbank auftritt? Hast du Connection Pooling aktiviert? Wie sieht dein Datenzugriff aus?

Q
Quero Themenstarter:in
12 Beiträge seit 2009
vor 13 Jahren

Passiert der zugriff über LAN oder Internet (verbindungsprobleme)?

Der Fehler tritt auch auf, wenn Datenbank, Hauptprogramm und Service auf einem Rechner laufen.

Machst du übermäßig viele Abfragen (Problem mit gleichzeitig offenen Connections) ?

Ja, da kommt schon was zusammen. Der Service nimmt Daten entgegen, bereitet sie in einem gewissen Rhythmus auf, dazu das Hauptprogramm, welches auch noch 6 Panels mit eigenen Cursorn hat. Im Extremfall können da schon bin zu 10 Cursor gegeneinander arbeiten.

Hast du schön Using-Blöcke und try/catch verwendet? Ja, allerdings bekomme ich nur diese Meldung über fehlende Objektinstanz.

Hast du Connection Pooling aktiviert? Hab ich einstellbar gehalten. Bei einem Fehlerfall ist das Pooling für Service und Hauptprogramm definitiv abgeschaltet.

Hier mal das wichtigste (connect und read) aus meiner Schnittstelle.


    public class SqlInterface
    {
        MySqlConnection connection = null;
        MySqlCommand myCommand = null;
        MySqlDataReader myReader = null;
        MySqlTransaction myTrans = null;

        int paramcounter = 0;

        bool autotrans = false;

        public const int TypInteger = 1;
        public const int TypDouble = 2;
        public const int TypString = 3;
        public const int TypDateTime = 4;

        private String myConnectionString = "";

        //public int affekted_rows = 0;

        /**
         * Connect
        */
        public bool Connect()
        {
            if (myConnectionString.Equals(""))
            {
                String pooling = LibGlobals.get_pooling();
                if (pooling.Equals("false"))
                    myConnectionString = "	DataBase = FMZ; Data Source = " + LibGlobals.get_ip_adresse() + "; User ID = abc; Password = xyz; Pooling=false";
                else
                    myConnectionString = "	DataBase = FMZ; Data Source = " + LibGlobals.get_ip_adresse() + "; User ID = abc; Password = xyz";
            }
            
            connection = new MySqlConnection();
            connection.ConnectionString = myConnectionString;
            try
            {
                connection.Open();
                if (connection.State == ConnectionState.Open)
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                connection.Close();
                connection = null;
                Logging.LogError("... beim Connect" + ex.Message);
            }
            return false;
        }


        /**
         * 
         * Disconnect
         *
         */
        public void Disconnect()
        {
            if (connection != null)
            {
                connection.Close();
                connection = null;
            }
        }

        /*
         * 
         *  PrepareRead
         * 
         */
        public bool PrepareRead(string SqlCommand)
        {
            try
            {
                myCommand = connection.CreateCommand();
                myCommand.Connection = connection;

                String strTemp = SqlCommand;
                int replacecounter = 0;
                while (true)
                {
                    int offset = strTemp.IndexOf("?");
                    if (offset >= 0)
                    {
                        replacecounter++;
                        String strReplace = "@p" + replacecounter;
                        String strNew = strTemp.Substring(0, offset);
                        strNew += strReplace;
                        strNew += strTemp.Substring(offset + 1, strTemp.Length - offset - 1);
                        strTemp = strNew;
                    }
                    else
                        break;
                }
                while (true)
                {
                    int offset = strTemp.IndexOf("@");
                    if (offset >= 0)
                    {
                        String strReplace = "?";
                        String strNew = strTemp.Substring(0, offset);
                        strNew += strReplace;
                        strNew += strTemp.Substring(offset + 1, strTemp.Length - offset - 1);
                        strTemp = strNew;
                    }
                    else
                        break;
                }
                myCommand.CommandText = strTemp;
                myReader = null;
                paramcounter = 0;
            }
            catch (Exception e)
            {
                Logging.LogError("Fehler beim PrepareRead, Datenbank nicht gefunden? " + e.Message);
                Logging.LogError("Kommando war " + SqlCommand);
                return false;
            }
            
            return true;
        }

        /*
         * 
         * 
         * 
         */
        public bool ReadNext()
        {
            try
            {
                if (myReader == null)
                    myReader = myCommand.ExecuteReader();
                return myReader.Read();
            }
            catch (Exception e)
            {
                try { Logging.LogError(myCommand.CommandText); }
                catch (Exception) { }
                Logging.LogError(e.Message);
                Logging.LogError("... beim ReadNext ");
            }
            return false;
        }

        /*
         * 
         * CloseRead
         * 
         */
        public void CloseRead()
        {
            try
            {
                myReader.Close();
                myReader = null;
            }
            catch (Exception) { }
        }
.
.
.
}

Eine Anwendung der Schnittstelle sieht dann so aus:


            SqlInterface si = = new SqlInterface();
            si.Connect();
            si.PrepareRead("select PLATZ_ART from PLATZ where NAME=?");
            si.AddParameter(SqlInterface.TypString, name);
            if (si.ReadNext())
            {
                platz_art = si.GetString(0);
            }
            si.CloseRead();
            si.Disconnect();

1.457 Beiträge seit 2004
vor 13 Jahren

Bitte schau dir folgende Artikelserie:
[Artikelserie] Parameter von SQL Befehlen

Q
Quero Themenstarter:in
12 Beiträge seit 2009
vor 13 Jahren

Ok, gelesen. Ich kann da aber nichts für mich finden.

Ich arbeite rein parametrisiert, ich hab noch einmal ein Schnipsel hinter den Schnittstellencode angehangen.

Evtl. war das nicht sofort erkennbar, weil die Schnittstelle zunächst auf auf ODBC ausgerichtet war ("UPDATE MyTable SET myValue = ?;") und dann auf MySQL Connector umgeschrieben wurde (alle "?" werden intern durch ein "@p" + counter ersetzt.

Für solche Umstellungen hab ich ja diese Klasse.

Die ganzen dort beschrieben Formatprobleme entfallen ja bei parametrisierter Übergabe schlagartig.

1.457 Beiträge seit 2004
vor 13 Jahren

Bei dir entstehen eher Problem, dass eine Connection oder ein DataReader oder was auch immer disposed wird und du es nochmal verwenden möchtest. An deiner Stelle würde ich deine Klasse komplett löschen.

Benutze lieber die DbCommon Klassen um eine Abstraktion für verschiedene Datenbanken zu erhalten. Hier musst du dann lediglich einen Command Text auch auslagern oder zusammenbauen lassen. Aber dann sind wir wieder beim Thema OR Mapper, und davon gibt es auch genügend 😉

Q
Quero Themenstarter:in
12 Beiträge seit 2009
vor 13 Jahren

Erstmal Danke für die Antworten.

Auf diese Klasse setzen 5 Jahre Arbeit auf, die kann ich nicht mal eben löschen.

Außerdem sagt mein Bauchgefühl weiterhin, dass diese Schnittstelle im Gunde ok ist.

Nach zwei Tagen googeln bin ich auf viele Probleme gestoßen, die den MySQLConnector betreffen, insbesondere in Anwendungen mit munterem Multithreading. Die Spekulationen und Ansätze waren vielfältig. Am häufigsten war ein Zusammenhang mit dem Pooling zu lesen.

Also den Pooling Parameter auf default gelassen (true) und siehe da: Kein Fehler seither.

Ganz befriedigend ist das natürlich nicht, das Pooling hab ich extra in der Anwendung abschaltbar gemacht, weil eine hoch belastete Installation dieser Anwendung manchmal so viele Connections im Pool hatte, dass irgendwann keine neuen mehr aufgemacht werden konnten.

Falls ich demnächst mehr wissen sollte, werde ich es hier posten.