Gespeicherte Prozedur kehrt manchmal kurz zurück und gibt manchmal int zurück

8

Ich arbeite mit einer Legacy-Codebasis und muss eine gespeicherte Prozedur aufrufen, die nicht geändert werden darf . Diese gespeicherte Prozedur gibt eine Zeile oder mehrere Zeilen von Validierungsdaten zurück.

Beispiel einer Ergebnismenge (zwei Spalten, Code und Text):

%Vor%

ODER

%Vor%

In der Prozedur selbst wird die Nachricht einfach wie folgt ausgewählt:

%Vor%

Problem:

Ich verwende Entity Framework, um das Ergebnis dieser gespeicherten Prozedur einer benutzerdefinierten Klasse zuzuordnen:

%Vor%

Der Anruf selbst:

%Vor%

Ich habe einige Integrationstests geschrieben und festgestellt, dass die 0 als short angezeigt wird, wenn die Prozedur die Erfolgsmeldung zurückgibt. Wenn eine Nachricht ungleich Null zurückgegeben wird, wird sie als int angezeigt. Ich nahm an, dass code als int gesetzt wurde, der Short würde passen. Leider bekomme ich die folgende Ausnahme für meinen Erfolgstest:

  

Die angegebene Umwandlung von einem materialisierten Typ 'System.Int16' in den Typ 'System.Int32' ist nicht gültig.

Wenn ich code auf einen kurzen Wert umschalte, um meinen Erfolgstest zu bestehen, schlägt mein Fehlertest mit der folgenden Ausnahme fehl:

  

Die angegebene Umwandlung von einem materialisierten Typ 'System.Int32' in den Typ 'System.Int16' ist nicht gültig.

ADO.NET ist eine Antwort

Eine Lösung besteht darin, auf das SqlDataReader -Objekt von ADO.NET zurückzugreifen, also habe ich das als Ausweichlösung. Ich frage mich, ob ich etwas auf der EF-Seite tun kann, um das funktionieren zu lassen.

    
Garrison Neely 24.03.2015, 20:24
quelle

7 Antworten

6

(Dies ist ein Follow-up zu meiner vorherigen Antwort . Es ist nur relevant für und höher.

Kurze Antwort:

%Vor%

Vorgehensweise in dieser Antwort:

Diese Antwort folgt in Ihren Fußstapfen und verwendet SqlQuery , um Ihre gespeicherte Prozedur auszuführen. (Warum nicht ein ganz anderer Ansatz? Weil es vielleicht keine Alternative gibt. Ich werde weiter unten darauf eingehen.)

Beginnen wir mit einer Beobachtung über Ihren aktuellen Code:

%Vor%

Der Abfragetext "old_sproc" wird für "EXECUTE old_sproc" wirklich als T-SQL abgekürzt. Ich erwähne das, weil es leicht zu denken ist, dass SqlQuery irgendwie den Namen einer gespeicherten Prozedur speziell behandelt; aber nein, das ist eigentlich eine normale T-SQL-Anweisung.

In dieser Antwort werden wir Ihr aktuelles SQL nur ein kleines bisschen ändern.

Implizite Typkonvertierungen mit der WITH RESULT SETS -Klausel:

Bleiben wir bei dem, was Sie bereits tun: EXECUTE die gespeicherte Prozedur über SqlQuery . Ab SQL Server 2012 unterstützt die EXECUTE -Anweisung eine optionale Klausel mit dem Namen WITH RESULT SETS , mit der Sie angeben können, welche Ergebnissätze voraussichtlich zurückgegeben werden. SQL Server versucht, implizite Typkonvertierungen durchzuführen, wenn die tatsächlichen Ergebnismengen nicht mit dieser Spezifikation übereinstimmen.

In Ihrem Fall könnten Sie das tun:

%Vor%

Die hinzugefügte Klausel besagt, dass Sie eine Ergebnismenge mit einer code INT - und einer text VARCHAR(MAX) -Spalte erwarten. Das wichtige Bit ist code INT : Wenn die gespeicherte Prozedur SMALLINT -Werte für code erzeugt, führt SQL Server die Konvertierung für Sie in INT durch.

Implizite Conversions könnten Sie noch weiter bringen: Sie könnten beispielsweise code als VARCHAR(…) oder sogar NUMERIC(…) angeben (und Ihre C # -Eigenschaften in string bzw. decimal ändern).

Wenn Sie die SqlQuery -Methode von Entity Framework verwenden, ist es unwahrscheinlich, dass Sie etwas Besseres bekommen.

Zum schnellen Nachschlagen finden Sie hier einige Zitate von der verknüpften MSDN-Referenzseite:

  

"Die tatsächliche Ergebnismenge, die während der Ausführung zurückgegeben wird, kann von dem mit der WITH RESULT SETS-Klausel definierten Ergebnis auf eine der folgenden Arten abweichen: Anzahl der Ergebnismengen, Anzahl der Spalten, Spaltenname, Nullwert, und Datentyp . "

     

"Wenn sich die Datentypen unterscheiden, wird eine implizite Konvertierung in den definierten Datentyp durchgeführt."

Muss ich eine SQL-Abfrage schreiben? Gibt es nicht einen anderen (mehr ORM) Weg?

Keine, die mir bekannt ist.

Das Entity Framework hat sich in der jüngsten Vergangenheit in einer "Code First" -Richtung entwickelt (es ist derzeit in der Version 6), und dieser Trend wird sich wahrscheinlich fortsetzen.

Das Buch "Programming Entity Framework Code First" von Julie Lerman & amp; Rowan Miller (veröffentlicht 2012 von O'Reilly) hat ein kurzes Kapitel "Arbeiten mit gespeicherten Prozeduren", das zwei Codebeispiele enthält; Beide verwenden SqlQuery , um die Ergebnismenge einer gespeicherten Prozedur zuzuordnen.

Wenn diese beiden EF-Experten keine andere Möglichkeit zeigen, gespeicherte Prozeduren abzubilden, dann bietet EF zur Zeit keine Alternative zu SqlQuery .

(PS .: Zugegebenermaßen ist das Hauptproblem des OP nicht gespeicherte Prozeduren per se; es macht EF eine automatische Typumwandlung durchzuführen. Selbst dann bin ich mir einer anderen Möglichkeit als der hier gezeigten nicht bewusst.)

    
stakx 26.03.2015, 22:55
quelle
5

Wenn Sie die gespeicherte Prozedur selbst nicht ändern können, könnten Sie eine gespeicherte Wrapper-Prozedur erstellen, die die Daten in irgendeiner Weise verändert und EF dazu veranlasst, dies zu tun.

Nicht ideal natürlich, aber vielleicht eine Option.

    
ozz 26.03.2015 16:16
quelle
2

( Hinweis: Wenn Sie mit SQL Server 2012 oder höher arbeiten, finden Sie unter mein Follow-up Antwort , die eine viel kürzere, schönere Art zeigt, das gleiche zu tun, was hier beschrieben wird.

Hier ist eine Lösung, die im EF-Land verbleibt und keine Datenbankschemaänderungen erfordert.

Da Sie jedes gültige SQL an die Methode SqlQuery übergeben können, hindert Sie nichts daran, ein Skript mit mehreren Anweisungen zu übergeben, das:

  1. DECLARE s eine temporäre Tabelle;
  2. EXECUTE s die gespeicherte Prozedur und INSERT s das Ergebnis in die temporäre Tabelle;
  3. SELECT s das Endergebnis dieser temporären Tabelle.

Im letzten Schritt können Sie weitere Nachbearbeitungen vornehmen, z. B. eine Typumwandlung.

%Vor%     
stakx 26.03.2015 16:30
quelle
0

Ändern Sie auf der Entity Framework-Datenmodelliererseite ( Model Browser ) entweder das funktionale Mapping in ein bestimmtes int, das für die ValidationResult -Klasse funktioniert, oder erstellen Sie ein neues functional mapping Ergebnisklasse, die das entsprechende int hat und diese als resultierende DTO-Klasse verwendet.

Ich verlasse diesen Prozess vage, weil ich keinen Zugriff auf die eigentliche Datenbank habe; Stattdessen stelle ich den Prozess bereit, um entweder eine neue funktionale Zuordnung zu erstellen oder eine vorhandene zu ändern. Trial and Error hilft Ihnen dabei, das inkorrekte funktionale Mapping zu überwinden.

Ein weiterer Trick, um EF die richtigen Informationen generieren zu lassen, ist, den gespeicherten proc vorübergehend zu löschen und einen neuen zu erhalten, der einen Stub zurückgibt, wie zum Beispiel:

%Vor%

Der Grund dafür ist, dass EF manchmal nicht feststellen kann, was die gespeicherte Prozedur letztendlich zurückgibt. Wenn dies der Fall ist, liefert das zeitweilige Erstellen des Stub Rückgabewerts und des Generierens von EF daraus ein klares Bild für die Zuordnungen. Dann bringt der sproc nach einem Update manchmal seinen ursprünglichen Code zurück.

    
OmegaMan 26.03.2015 23:44
quelle
-1

Ignoriere das int / short. der text ist immer gleich für die gleiche nummer richtig? bekomme nur den Text. habe einen Schalterfall. Ja, es ist ein Hack, aber wenn Sie nicht die Wurzel des Problems beheben können (und Sie sagen, dass Sie nicht erlaubt sind), dann sollten Sie mit dem Hack gehen, der die geringste Zeit für die Erstellung braucht und keine Probleme auf der Straße verursachen wird Nächste Person, die den Code verwaltet. Wenn dieses gespeicherte Proc veraltet ist, wird es in der Zukunft keine neuen Arten von Ergebnissen geben. und diese Lösung zusammen mit einem netten Kommentar löst das und lässt Sie wieder irgendwo anders arbeiten.

    
user1852503 26.03.2015 23:33
quelle
-2

Übergeben Sie den statischen Nachrichtencode an ein int:

%Vor%

Dies stellt sicher, dass das zurückgegebene Literal mit dem von der anderen Abfrage zurückgegebenen int konsistent ist. Belassen Sie ValidationResult.code als int deklariert.

Hinweis: Ich weiß, dass ich den Teil in der Frage über den SP nicht geändert habe, aber da dies die Antwort ziemlich kompliziert macht, überlasse ich das hier für andere, die das gleiche Problem haben könnten , sind aber in der Lage, es viel einfacher zu lösen, indem Sie den SP ändern. Das funktioniert, wenn Sie eine Inkonsistenz des Rückgabetyps im SP haben und das Modifizieren eine Option ist.

    
AaronLS 24.03.2015 20:31
quelle
-3

Es gibt eine Problemumgehung, die Sie verwenden könnten, wenn Sie keine bessere Lösung finden. Sei es ein Int. Es wird für alle Fehlercodes funktionieren. Wenn Sie eine Ausnahme erhalten, wissen Sie, dass das Ergebnis erfolgreich war. Daher können Sie eine try / catch für diese spezifische Ausnahme hinzufügen. Es ist nicht schön und abhängig davon, wie viel es läuft, kann es die Leistung beeinträchtigen.

Eine andere Idee, haben Sie versucht, den Typ von code in object zu ändern?

    
Mikael Eliasson 26.03.2015 16:19
quelle

Tags und Links