2009-01-30 5 views
8

Ich benutze Linq-to-SQL mit einem SQL Server-Backend (natürlich) als ORM für ein Projekt. Ich muss die Ergebnismenge von einer gespeicherten Prozedur abrufen, die von einer dynamisch erstellten Tabelle zurückgegeben wird. Hier ist, was die Prozedur wie folgt aussieht:Wie bekomme ich Linq zu SQL, um die Ergebnismenge einer dynamischen Stored Procedure zu erkennen?

CREATE procedure [RetailAdmin].[TitleSearch] (
@isbn varchar(50), @author varchar(50), 
@title varchar(50)) 
as 

declare @L_isbn varchar(50) 
declare @l_author varchar(50) 
declare @l_title varchar(50) 
declare @sql nvarchar(4000) 

set @L_isbn = rtrim(ltrim(@isbn)) 
set @l_author = rtrim(ltrim(@author)) 
set @l_title = rtrim(ltrim(@title)) 

CREATE TABLE #mytemp(
    [storeid] int not NULL, 
    [Author] [varchar](100) NULL, 
    [Title] [varchar](400) NULL, 
    [ISBN] [varchar](50) NULL, 
    [Imprint] [varchar](255) NULL, 
    [Edition] [varchar](255) NULL, 
    [Copyright] [varchar](100) NULL, 
    [stockonhand] [int] NULL 
) 

set @sql = 'select a.storeid, Author,Title, thirteendigitisbn ISBN, 
Imprint,Edition,Copyright ,b.stockonhand from ods.items a join ods.inventory b on  
a.itemkey = b.itemkey where b.stockonhand <> 0 ' 

if len(@l_author) > 0 
set @sql = @sql + ' and author like ''%'[email protected]_author+'%''' 

if len(@l_title) > 0 
set @sql = @sql + ' and title like ''%'[email protected]_title+'%''' 

if len(@L_isbn) > 0 
set @sql = @sql + ' and thirteendigitisbn like ''%'[email protected]_isbn+'%''' 

print @sql 

if len(@l_author) <> 0 or len(@l_title) <> 0 or len(@L_isbn) <> 0 

begin 
    insert into #mytemp 
    EXECUTE sp_executesql @sql 
end 


select * from #mytemp 
drop table #mytemp 

Ich habe dieses Verfahren nicht schreiben, sondern kann auch eine Änderung beeinflussen kann, wenn es ein wirklich ernstes Problem.

Mein derzeitiges Problem ist, dass, wenn ich diese Prozedur zu meinem Modell hinzufügen, die Designer diese Funktion erzeugt:

[Function(Name="RetailAdmin.TitleSearch")] 
public int TitleSearch([Parameter(DbType="VarChar(50)")] string isbn, 
    [Parameter(DbType="VarChar(50)")] string author, 
    [Parameter(DbType="VarChar(50)")] string title) 
{ 
    IExecuteResult result = this.ExecuteMethodCall(this, 
     ((MethodInfo)(MethodInfo.GetCurrentMethod())), isbn, author, title); 

    return ((int)(result.ReturnValue)); 
} 

die nichts sieht aus wie die Ergebnismenge ich bekomme, wenn ich das proc manuell ausführen:

Result Set

Kann mir jemand sagen, was falsch ist hier los?

Dies ist im Grunde das gleiche Problem wie this question, aber aufgrund der schlechten Formulierung von der OP wurde es nie wirklich beantwortet.


Danke Marc für Ihre Antwort. Ich werde über die von Ihnen vorgeschlagenen Änderungen nachdenken.

Das Problem war die Temp-Tabelle. Linq zu Sql weiß einfach nicht, was sie damit machen sollen. Dies war besonders schwierig zu diagnostizieren, da Visual Studio Informationen über gespeicherte Prozeduren zwischenspeichert. Wenn es zum ersten Mal fehlschlug, eine Ergebnismenge zu finden, setzte es die Rückgabe als Standard-Integer-Typ und wurde nicht aktualisiert, wenn ich Änderungen an der gespeicherten Prozedur vornahm. VS immer eine Veränderung zu erkennen, erfordert, dass Sie zu:

  • proc Löschen aus dem dbml
  • die Serververbindung vom Server-Explorer löschen
  • die dbml speichern eine erneute Kompilierung zu zwingen
  • das Projekt schließen und neu starten, VS
  • die Serververbindung neu erstellen und das proc importieren

Sie werden vielleicht nicht jeder dieser Schritte zu tun haben, aber tha Das hat für mich funktioniert. Wenn Sie eine temporäre Tabelle verwenden müssen, müssen Sie einen Barebone-Prozess erstellen, der einfach das richtige Schema zurückgibt und dann nach dem Importieren in den OR-Designer die gewünschten Änderungen vornimmt.

Antwort

5

Erste - WICHTIG - Ihr SQL ist anfällig für die Injektion;

if len(@l_author) > 0 
set @sql = @sql + ' and author like ''%''[email protected]+''%''' 

EXECUTE sp_executesql @sql, N'@author varchar(100)', @L_author 

Dieser übergibt den Wert von @L_author in als @author Parameter im dynamischen Befehl - Injection-Angriffe zu verhindern: die innere Befehl sollte parametriert werden.


Zweitens - Sie brauchen nicht wirklich die temporäre Tabelle. Es tut nichts für Sie ... Sie nur EINFÜGEN und AUSWÄHLEN. Vielleicht einfach EXEC und lass die Ergebnisse natürlich zum Anrufer fließen?

Unter anderen Umständen wäre eine Tabellenvariable besser geeignet, aber dies funktioniert nicht mit INSERT/EXEC.


Sind die Spalten für jeden Anruf identisch? Wenn dies der Fall ist, schreibe entweder den dbml manuell oder verwende einen temporären SP (nur mit "WHERE 1 = 0" oder etwas), so dass SET FMT_ONLY ON funktionieren kann.

Wenn nicht (verschiedene Spalten pro Verwendung), dann gibt es keine einfache Antwort. Verwenden Sie in diesem Fall möglicherweise normales ADO.NET (ExecuteReader/IDataReader - und vielleicht sogar DataTable.Fill).

Natürlich könnten Sie LINQ lassen die Belastung nehmen ... (C#):

... 
if(!string.IsNullOrEmpty(author)) { 
    query = query.Where(row => row.Author.Contains(author)); 
} 
... 

etc

+0

Die ersten beiden Punkte sind wichtig, um diese besondere Frage, aber im Allgemeinen denke ich [SET FMTONLY ON] [http://msdn.microsoft.com/en-us/library/ms173839.aspx] ist ein wichtiger Teil des Problems, wenn LINQ to SQL generierten Code die erwarteten Ergebnisse nicht zurückgibt. In einigen Fällen habe ich es auf OFF gesetzt [http://www.fishofprey.com/2009/08/detecting-whenni-nettiers-is.html], wenn ich weiß, dass das gespeicherte Proc auf keinen Einfluss haben wird die Daten. I.e. Wählt nur aus. –

5

Es gibt keine wirklich einfache Möglichkeit, dies zu tun. Ich hatte in der Vergangenheit das gleiche Problem. Ich denke, das Problem ist, dass Linq to Sql keine Möglichkeit hat herauszufinden, welcher Typ zurückgegeben wird, da Sie die SELECT-Anweisung zur Ausführungszeit aufbauen. Was ich getan habe, um dies zu umgehen, war in der gespeicherten Proc, ich habe nur eine Auswahl und wählte alle Spalten, die ich möglicherweise benötigt. Dann hatte ich Linq zu Sql die Funktion basierend darauf generieren. Dann ging ich zurück zu SQL und änderte den gespeicherten Prozess zurück, so wie er sein sollte. Der Trick besteht darin, Ihre DBML nicht neu zu generieren.

Verwandte Themen