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
Du bist lang genug dabei: Benutze die richtige Code- und Error-Tags und wähle einen richtigen Titel. "Problem mit xyz" kann alles sein!
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:
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!
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.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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...
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?
Hallo Grimmbizkit,
Was ist das genau, Repository Pattern und Single Responsibility?
Single responsibility principle
Gruss
Coffeebean
Microsoft MVP // Me // Blog // GitHub // @Egghead // All my talks // Speakerdeck
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:
@Palin
Der ctahc-Block ist normal mit einem Rollback gefüllt, habe ich hier nur weggelassen
Pooling wird im Connectionstring angegeben.
www.connectionstrings.com
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
@ Abt
Ohne das GC.Collect() wird es ehr schlimmer, der gleiche Fehler kommt direkt. Nach 2-3 Aufrufen
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
Microsoft MVP // Me // Blog // GitHub // @Egghead // All my talks // Speakerdeck
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.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
@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);
}
}
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).
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
@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);
}
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 😉
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Das Pooling ist wohl beim Copy&Paste entfallen, habe ich drin.
Ehrlich gesagt verliere ich so langsam den kompletten überblick 😦