Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   C# SQL MessageId aus einer SQLCLR Funktion (https://www.delphipraxis.net/84339-sql-messageid-aus-einer-sqlclr-funktion.html)

Jelly 15. Jan 2007 16:18

Datenbank: MSSQL • Version: 2005

SQL MessageId aus einer SQLCLR Funktion
 
Ich habe hier ein Problem mit den SQL Fehlercodes. Ich erklär erst einmal was ich überhaupt mache:

im SQL Server 2005 nutze ich eine .NET Funktion (SQLCLR). Innerhalb dieser .NET Funktion greife ich auf einen Webservice zu, der mir einen Wert einer bestimmten Berechnung zurückliefert. Diesen Wert soll die .NET Funktion dann an den Aufrufer zurückgeben.

Das klappt auch alles so wie es soll, ne richtig feine Sache. Nur habe ich zur Zeit Probleme mit der Fehlerbehandlung. Tirtt im Webservice ein Fehler auf, so knallts mit in der .NET Funktion (in C# geschrieben), welche mir dann den Fehler an den SQL Server zurückliefert. Der sieht dann in etwa so aus:

Msg 6522, Level 16, State 1, Line 2
A .NET Framework error occurred during execution of user defined routine or aggregate 'ReadInt':
System.Web.Services.Protocols.SoapException: Server was unable to process request. -->
Logical Itemname n12 for Device rbg does not exist in loaded itemslist.
System.Web.Services.Protocols.SoapException:
at System.Web.Services.Protocols.SoapHttpClientProtoc ol.ReadResponse(SoapClientMessage message, WebResponse response, Stream responseStream, Boolean asyncCall)
at System.Web.Services.Protocols.SoapHttpClientProtoc ol.Invoke(String methodName, Object[] parameters)
at TOPCService.ReadInt(String Itemname, String DeviceName)
at Functions.ReadInt(String Itemname, String DeviceName)
.

Aus dieser langen Fehlermeldung hab ich mal die wichtigen Dinge blau markiert. Der Fehlermeldungstext wird aus dem Webservice gesetzt, jedoch habe ich da natürlich keine Möglichkeit, die Msg Id zu setzen. Bei jedem Fehler, der auftritt, schmeisst mir der SQL Server immer nur die Msg 6522 zurück. Aber genau diesen Fehler muss ich auswerten, und zwar so effizient wie nur möglich.

Ich dachte daran, in der .NET Funktion in C# den Fehler im catch Block abzufangen, und dort aufgrund des Fehlertextes "Logical Itemname n12 for Device rbg does not exist in loaded itemslist." diesen auszuwerten und eine benutzerdefinierte Msg Id zurückzuliefern. Somit hätte ich eine bessere Flexibilität bei der Auswertung des Fehlers im SQL Server.

Und genau da häng ich. Die C# Funktion sieht zur Zeit so aus:
Code:
public static String ReadString(String Itemname, String DeviceName)
    {
        String S;
        TOPCService OPC;
        try
        {
            OPC = new TOPCService();
            OPC.Url = RegSettings.UrlWebService();
            S = OPC.ReadString(Itemname, DeviceName);
            OPC.Dispose();
        } catch (Exception e) {
            S = "";
            ArgumentException ec = new ArgumentException("Item not found");
            throw ec;
        }

        return (String)S;
    }
Nur krieg ich mit dem Code natürlich die MsgId nicht geändert. Ich müsste im Catch Block den Fehler analysieren können und eine entsprechende exception auslösen. In T-SQL ginge das mit
SQL-Code:
RAISEERROR MsgId,-1,-1
aber in .NET ?

Ich hoff mein Problem wurde verstanden :wiejetzt:

Elvis 16. Jan 2007 09:38

Re: SQL MessageId aus einer SQLCLR Funktion
 
Du kannst RaiseError als Command durch den Context abschicken. Du musst allerdings den Fehler, den RaiseError innerhalb der .Net Sproc auslöst abfangen und schlucken.
Dann könnte es funktionieren:
Code:
static void RaiseError(int messageId, int severity, int state)
{
  using (SqlCommand command = new SqlCommand("RaiseError(@messageId, @severity, @state)"))
  {
    command.Parameters.AddWithValue("messageId", messageId);
    command.Parameters.AddWithValue("severity", severity);
    command.Parameters.AddWithValue("state", state);
   
    try
    {
      SqlContext.Pipe.ExecuteAndSend(command);
    }
    catch {/*nüschts*/}
  }
}

static void RaiseError(string messageText, int severity, int state)
{
  using (SqlCommand command = new SqlCommand("RaiseError(@messageText, @severity, @state)"))
  {
    command.Parameters.AddWithValue("messageText", messageText);
    command.Parameters.AddWithValue("severity", severity);
    command.Parameters.AddWithValue("state", state);

    try
    {
      SqlContext.Pipe.ExecuteAndSend(command);
    }
    catch {/*nüschts*/}
  }
}
Keine Ahnung, ob das so stimmt. TSQL und das Fehlen von Exceptions darin sind so abartig... :kotz:

Jelly 16. Jan 2007 10:02

Re: SQL MessageId aus einer SQLCLR Funktion
 
Zitat:

Zitat von Elvis
Keine Ahnung, ob das so stimmt. TSQL und das Fehlen von Exceptions darin sind so abartig... :kotz:

Danke für deine Mühe. Die Sache hat allerdings 2 Haken:
1. In einer SQLCLR Funktion kann ich nichts über die Pipe senden, weil das in Funktionen leider und unverständlicherweise nicht erlaubt ist
2. Die Fehler sind nach meinem Wissen kummulativ. Durch den Raiseerror würde ich dann wohl höchstens einen weiteren Fehler übergeben. Aber damit könnte ich zur Not noch leben, denn die Variabel @@ERROR enthält ja wohl die zuletzt geworfene Exception.

Jelly 16. Jan 2007 10:17

Re: SQL MessageId aus einer SQLCLR Funktion
 
OK, das mit dem RAISERROR innerhalb einer Funktion (egal ob .NET oder T-SQL) ist auch unterbunden. Die Fehlermeldung vom SQL Server lautet:
Code:
Msg 6522, Level 16, State 1, Line 4
Invalid use of side-effecting or time-dependent operator in 'RAISERROR' within a function.
:wall:
Das kann doch nicht sein, dass ich aus einer Funktion heraus dem Aufrufer keinen Fehler übermitteln kann.

Elvis 16. Jan 2007 10:31

Re: SQL MessageId aus einer SQLCLR Funktion
 
Hab' mich gerade mal schlau gemacht. Anscheinend gibt es keine hübsche Lösung dafür.
Das Errorhandling von TSQL ist einfach zu schrottig. Wie wär's mit einem Output parameter, der den Error code angibt?
Dann kannst du die CLR SProc in eine TSQL SProc verpacken, in der kannst du dann RaiseError ausführen. (An deiner Stelle wäre ich spätestens ab diesem Absatz fuchsteufelswild.... :zwinker: )
Tja, was bringt einem CLR Integration wenn das DBMS noch genauso dämlich ist wie 2000? :wall:

Jelly 16. Jan 2007 13:35

Re: SQL MessageId aus einer SQLCLR Funktion
 
Zitat:

Zitat von Elvis
Hab' mich gerade mal schlau gemacht. Anscheinend gibt es keine hübsche Lösung dafür.

Ich zwischenzeitlich auch, und das scheint alles nicht so toll zu sein.

Zitat:

Zitat von Elvis
Das Errorhandling von TSQL ist einfach zu schrottig.

naja, immerhin kann man jetzt wenigstens gegenüber der 2000er Version die Fehler in einem Try...catch Block abfangen.

Zitat:

Zitat von Elvis
Wie wär's mit einem Output parameter, der den Error code angibt?

Das Problem ist, dass einiges als Funktion deklariert sein muss, da die Funktion weiter in Select-Abfragen genutzt werden soll. Es hilft mir leider nicht weiter eine T-SQL funktion zu deklarieren, die intern ein Stored Procedure aufruft, denn dann hab ich wieder die gleichen Probleme mit dem Raise Error weil der .NET Aufruff im Kontext der T-SQL Funktion aufgerufen wird.

Aber dein obiger Ansatz funktioniert... Für Stored Procedures. Neben lesenden (über Funktion) Zugriff auf Funktionen gibts auch schreibende Stored Procedures, die mit dem gleichem Webservice kommunizieren. Da krieg ich die Fehler abgefangen und kann sie an den Aufrufer mittels raiserror dort auch genau so anzeigen und auswerten.

Zitat:

Zitat von Elvis
Tja, was bringt einem CLR Integration wenn das DBMS noch genauso dämlich ist wie 2000? :wall:

Das DBMS an sich wird dadurch nicht schlecht. Die .NET Integration ermöglicht uns hier zumindest Dinge, die unter 2000 ganz einfach nicht möglich waren.

Elvis 16. Jan 2007 13:41

Re: SQL MessageId aus einer SQLCLR Funktion
 
Zitat:

Zitat von Jelly
naja, immerhin kann man jetzt wenigstens gegenüber der 2000er Version die Fehler in einem Try...catch Block abfangen.

lol! Naja, wenn man mit MSSQL leben muss mag es einige (fast) sinnvolle Neuerungen im 2005'er geben.
Ich konnte zum Glück jemanden finden, der mir den MSSQL-Krempel freiwillig abgenommen hat. :)
Nach 3 Monaten war ich reif für die Klapse... :wall:


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:10 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz