Laden...

Webservice auf MONO/Linux ist nach einiger Zeit nicht mehr verfügbar aber durch Aufruf reaktivierbar

Erstellt von Grimmbizkit vor 7 Jahren Letzter Beitrag vor 7 Jahren 2.553 Views
G
Grimmbizkit Themenstarter:in
308 Beiträge seit 2006
vor 7 Jahren
Webservice auf MONO/Linux ist nach einiger Zeit nicht mehr verfügbar aber durch Aufruf reaktivierbar

Moin zusammen,
ich habe mal eine Frage zum Thema Webservice unter Mono/Linux.

Kurz zum Problem:
Auf einer Linux VM läuft mein Webservice (er führt Datenbank befehle aus).

Aber zwischendurch (keine regelmäßigen Abstände) bekomme ich die Exception:> Fehlermeldung:

System.ServiceModel.FaultException: System.Data.SqlClient.SqlException: Server closed the connection. —-> Mono.Data.Tds.Protocol.TdsInternalException: Server closed the connection. —-> System.IO.IOException: Connection lost
at Mono.Data.Tds.Protocol.TdsComm.GetPhysicalPacketHeader () [0x00031] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/TdsComm.cs:630
at Mono.Data.Tds.Protocol.TdsComm.GetPhysicalPacket () [0x00000] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/TdsComm.cs:604
at Mono.Data.Tds.Protocol.TdsComm.GetByte () [0x00011] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/TdsComm.cs:477
at Mono.Data.Tds.Protocol.Tds.ProcessSubPacket () [0x00000] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs:1767
at Mono.Data.Tds.Protocol.Tds.NextResult () [0x0004a] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs:619
at Mono.Data.Tds.Protocol.Tds.SkipToEnd () [0x00005] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs:692
—- End of inner exception stack trace —-
at Mono.Data.Tds.Protocol.Tds.SkipToEnd () [0x0001d] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds.cs:695
at Mono.Data.Tds.Protocol.Tds70.ExecRPC (TdsRpcProcId rpcId, System.String sql, Mono.Data.Tds.TdsMetaParameterCollection parameters, Int32 timeout, Boolean wantResults) [0x000ad] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds70.cs:487
at Mono.Data.Tds.Protocol.Tds80.Execute (System.String commandText, Mono.Data.Tds.TdsMetaParameterCollection parameters, Int32 timeout, Boolean wantResults) [0x0003e] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/Mono.Data.Tds/Mono.Data.Tds.Protocol/Tds80.cs:229
at System.Data.SqlClient.SqlCommand.Execute (Boolean wantResults) [0x0020b] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/System.Data/System.Data.SqlClient/SqlCommand.cs:536
—- End of inner exception stack trace —-
at System.Data.SqlClient.SqlCommand.Execute (Boolean wantResults) [0x00252] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/System.Data/System.Data.SqlClient/SqlCommand.cs:542
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery () [0x00015] in /var/tmp/portage/dev-lang/mono-2.10.9-r2/work/mono-2.10.9/mcs/class/System.Data/System.Data.SqlClient/SqlCommand.cs:571
at HvsWerkApplAPI.Service1.InsertLieferschein (HvsWerkApplAPI.Responses.Lieferschein lfs, NummernKreis nrKreis) [0x00cce] in c:\SVN\Hvs\HVS.ng\WebServices\HvsWerkApplAPI\HvsWerkApplAPI\Service1.asmx.cs:4220

Nach einem öffnen der "Webseite" des Webservice geht es wieder.
Als wenn der Webservice in eine Art Standby Modus geht.

Webservice und Datenbank liegen auf dem gleichen PC

Hinweis von Coffeebean vor 7 Jahren

Du bist lang genug dabei: Benutze die richtige Code- und Error-Tags und wähle einen richtigen Titel. "Problem mit xyz" kann alles sein!

P
1.090 Beiträge seit 2011
vor 7 Jahren

Also für mich sieht es so aus als ob der SQL Server die Connection schließt.

Fehlermeldung:
System.ServiceModel.FaultException: System.Data.SqlClient.SqlException: Server closed the connection

Öffnest du bei jedem Aufruf eine neue Connection und schießt sie nach der Verwendung?

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

G
Grimmbizkit Themenstarter:in
308 Beiträge seit 2006
vor 7 Jahren

Hi,

Also die Verbindung hole ich mir hierher


public System.Data.SqlClient.SqlConnection GetSqlConnection()
{
    GC.Collect();
    System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection("Server=127.0.0.1;Database=.....");
    conn.Open();
    return (conn);
}

Diese Methode nutze ich dann wie folgt:


public Int32 InsertLieferschein(Responses.Lieferschein lfs, MSSQL.NummernKreis nrKreis)
{
    System.Guid guId = System.Guid.NewGuid();
    using (System.Data.SqlClient.SqlConnection conn = this.sql.GetSqlConnection())
    {
        System.Data.SqlClient.SqlCommand cmd1 = null;
        System.Data.SqlClient.SqlCommand cmd2 = null;
        cmd1 = conn.CreateCommand();
        cmd1.CommandText = "INSERT INTO ...";
        cmd1.Parameters.AddWithValue("@pLf...",guId);
        // Rest des Befehl...

        cmd2 = conn.CreateCommand();
        cmd2.CommandText = "INSE....";
        cmd2.Parameters.AddWithValue("@pLf...",guId);
        // Rest des Befehl...

        System.Data.SqlClient.SqlTransaction transaction = null;
        try
        {
            transaction = conn.BeginTransaction(IsolationLevel.Serializable);

            cmd2.Transaction = transaction;
            cmd1.Transaction = transaction;

            cmd1.ExecuteNonQuery();
            cmd2.ExecuteNonQuery();

            transaction.Commit();

            return lfsNr;
        }
        catch (Exception ex)
        {
        }
    }
}

Aus meiner sich ok oder?

Danke schon mal für die Hilfe!

16.806 Beiträge seit 2008
vor 7 Jahren
  • Das GC.Collect ist unnötig, besonders hier sogar kontraproduktiv (When to call GC.Collect() - Rule #1: dont!
  • Pooling aktiviert?
  • Stichwort Repository Pattern und Single Responsibility wären hier als Stichwort angebracht. Eine Methode sollte nicht selbstständig die Verbindung aufbauen, sondern eine vorhandene Verbindung nutzen

Zudem sieht es mir nach einer Schichtverletzung aus, wenn Du einer Methode zwei verschiedene Objekte unterschiedlicher Anwendungsschichten gibst (Business Models und Entitäten) im DAL nutzt.
Das ist aber ein Architekturproblem, nicht der Auslöser der Fehlermeldung.

G
Grimmbizkit Themenstarter:in
308 Beiträge seit 2006
vor 7 Jahren

Hallo Abt,

Pooling aktiviert? -> Wo?

Was ist das genau, Repository Pattern und Single Responsibility?

Die Methode baut eine neue Verbindung auf, weil ich diese von verschiedenen Mehtoden nutze und der Code so nur einmal habe. Auch weil ich während ich mit einem DataReader arbeite neue Daten abfragen muss. Oder gibt es da eine bessere Lösung?

Hier ein Beispiel was ich meine:


Responses.StKunde kunde = null;

using (System.Data.SqlClient.SqlConnection conn = this.sql.GetSqlConnection())
{
    System.Data.SqlClient.SqlCommand cmd = conn.CreateCommand();
    cmd.CommandText = "SELECT * FROM StKunden WHERE isdeleted = 0 AND KdNr = @pKdNr";
    cmd.Parameters.AddWithValue("@pKdNr", kdNr);

    System.Data.SqlClient.SqlDataReader reader = cmd.ExecuteReader();
                
    while (reader.Read())
    {
        kunde = new Responses.StKunde();

        for (Int32 indexCol = 0; indexCol < reader.FieldCount; indexCol++)
        {
            if (reader.IsDBNull(indexCol))
                continue;

            switch (reader.GetName(indexCol))
            {
                // STRING
                case "KdNr": kunde.KdNr = reader.GetString(indexCol); break;
                case "Name1": kunde.Name1 = reader.GetString(indexCol); break;
                //....
                case "AbsatzArtNr": kunde.AbsatzArt = this.GetAbsatzArt(kunde.AbsatzArtNr); break;
		//
		// REST DER METHODE...


W
955 Beiträge seit 2010
vor 7 Jahren

Es könnte vllt daran liegen dass es aufgrund des Isolationslevels Serializable zu einer Verklemmung der Transaktionen auf dem Server kommt. Funktioniert es denn mit einem geringerem Level?

2.207 Beiträge seit 2011
vor 7 Jahren

Hallo Grimmbizkit,

Was ist das genau, Repository Pattern und Single Responsibility?

The Repository Pattern

Single responsibility principle

Gruss

Coffeebean

P
1.090 Beiträge seit 2011
vor 7 Jahren

Den leeren Catch Block solltest du auch entfernen, bzw da ein Rollback der Transaktion machen, damit diese beendet wird und die Connection wider Freigeben kann und den Fehler Loggen.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

G
Grimmbizkit Themenstarter:in
308 Beiträge seit 2006
vor 7 Jahren

@Palin
Der ctahc-Block ist normal mit einem Rollback gefüllt, habe ich hier nur weggelassen

16.806 Beiträge seit 2008
vor 7 Jahren

Pooling wird im Connectionstring angegeben.
www.connectionstrings.com

G
Grimmbizkit Themenstarter:in
308 Beiträge seit 2006
vor 7 Jahren

@ Abt

Ohne das GC.Collect() wird es ehr schlimmer, der gleiche Fehler kommt direkt. Nach 2-3 Aufrufen

2.207 Beiträge seit 2011
vor 7 Jahren

Hallo Grimmbizkit,

dann weist es dich noch direkter auf einen vorhandenen Fehler hin. Das ist gut! Das schlimmste sind Fehler, die schwer nachzustellen sind oder nur sporadsich auftreten. Wenn ein Fehler wenigstens regelmässig kommt, hat man schonmal viel gewonnen 😉

Wieso holst du das conn.open() nichtmal in das using? Der, der die Connection aufmacht sollte sie auch schliessen. Hier macht A die Connection auf und B (durch das Using) wieder zu.

Was ist this.sql? Ist das ein per DI verfügbarer Service, der scoped ist? Oder ist das ein Singleton irgendwo? Ich vermute mal, dass es da ganz woanders - was grundlegendes - nicht stimmt...

Gruss

Coffeebean

16.806 Beiträge seit 2008
vor 7 Jahren

Ohne das GC.Collect() wird es ehr schlimmer

Dann ist bei euch was mächtig im Murks und wahrscheinlich der Grund dieses Problems hier. Mit GC.Collect kaschiert ihr das ganze, löst es aber nicht.
Löst den Grund und deckt ihn nicht einfach ab.

Lies den Link dazu. Deswegen hab ich ihn Dir gegeben.

G
Grimmbizkit Themenstarter:in
308 Beiträge seit 2006
vor 7 Jahren

@Coffeebean:

this.sql
ist nur eine Klasse damit ich die SqlBefehle nur einmal schreibe.


    public class MSSQL
    {
        public MSSQL()
        {

        }

        public System.Data.SqlClient.SqlConnection GetSqlConnection()
        {
            //GC.Collect();
            System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection("Server=127.0.0.1;Database=...;");

            //conn.Open();
            return (conn);
        }
    }

16.806 Beiträge seit 2008
vor 7 Jahren

Ja, der Code ist aber schon verbesserungswürdig.


public interface IMyDbProvider
{
     IDbConnection GetConnection();
}


public class MyMssqlProvider : IMyDbProvider
{
   public MyMssqlProvider (string connectionString) // Dependency Injection!
   {
   }

   public override IDbConnection GetConnection()
   {
      return new SqlConnection(_connectionString);
   }
}

Und entsprechend in der aufrufenden Klasse das Interface nutzen.

Bleibt dabei, dass ihr ein Grundlegendes Problem habt, das ihr lösen solltet.
GC.Collect als "Fix" zu sehen ist falsch (und fahrlässig).

G
Grimmbizkit Themenstarter:in
308 Beiträge seit 2006
vor 7 Jahren

@Abt

So läuft der Code bei mir.


    public interface IMyDbProvider
    {
        IDbConnection GetConnection();
    }
    
    public class MyMssqlProvider : IMyDbProvider
    {
        String connectionString;

        public MyMssqlProvider(string _connectionString) // Dependency Injection!
        {
            connectionString = _connectionString;
        }

        IDbConnection IMyDbProvider.GetConnection()
        {
            return new SqlConnection(connectionString);
        }
    }

Und der Using aufruf ist wohl auch Suboptimal:


using (System.Data.SqlClient.SqlConnection conn = (System.Data.SqlClient.SqlConnection) sql.GetConnection())
            {
                conn.Open();
                ...
            }

in der Klasse dann:


// DIESER CODE
        public System.Data.IDbConnection GetConnection()
        {
            System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection("Server=127.0.0.1;Database=xxxxxxxxxx;User Id=sa;Password=xxxxxxxxxx;Pooling=false;");
            return conn;
        }
// STATT DIESEM
        public System.Data.SqlClient.SqlConnection GetSqlConnection()
        {
            //GC.Collect();
            System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection("Server=127.0.0.1;Database=xxxxxxxxxx;User Id=sa;Password=xxxxxxxx;Pooling=false;");
            return conn;
            
            //conn.Open();

            //if (conn.State != System.Data.ConnectionState.Open)
            //    throw new Exception("ConnectionState = " + conn.State.ToString());

            //return (conn);
        }

16.806 Beiträge seit 2008
vor 7 Jahren

Ich habe die Anmeldedaten in Deinem Code unkenntlich gemacht.
Du solltest sie jetzt trotzdem ändern...

Da seh ich direkt:
Deine Anwendung läuft als SQL Admin. Sowas macht man nicht.
Dein Pooling ist aus. Warum, sieht man hier nicht. Sinn?

Und zu meinem Code:
Die Interfaces waren Absicht und es sollte nur das grundlegend korrekte Vorgehen zeigen.

using (System.Data.SqlClient.SqlConnection conn = (System.Data.SqlClient.SqlConnection) sql.GetConnection())

zeigt mir aber, dass Du das Prinzip von Interfaces nicht so ganz durchschaut hast 😉

G
Grimmbizkit Themenstarter:in
308 Beiträge seit 2006
vor 7 Jahren

Das Pooling ist wohl beim Copy&Paste entfallen, habe ich drin.

Ehrlich gesagt verliere ich so langsam den kompletten überblick 😦