2009-01-30 37 views
8

我正在使用Linq-to-SQL和SQL Server後端(當然)作爲項目的ORM。我需要從一個從動態創建的表中返回的存儲過程中獲得結果集。這裏的PROC的樣子:如何讓Linq to SQL識別動態存儲過程的結果集?

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 

我沒有寫這個程序,但可能是能夠影響的變化,如果有一個很嚴重的問題。

我現在的問題是,當我加入這個過程,我的模型,設計器生成此功能:

[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)); 
} 

它看起來並不像結果集時,我手動運行PROC我得到任何東西:

Result Set

誰能告訴我這是怎麼回事錯在這裏?

這與this question基本上是相同的問題,但由於從OP不佳的措辭,它從來沒有真正的答案。


感謝馬克您的回覆。我將看到有關進行您所建議的更改。

問題是臨時表。 Linq to Sql只是不知道該怎麼處理它們。這是特別難以診斷的,因爲Visual Studio會緩存有關存儲過程的信息,所以當它最初無法找到結果集時,它會將返回值設置爲默認整數類型,並且在更改存儲過程時不更新。獲取VS認識的改變需要你:

  • 從DBML刪除PROC
  • 刪除服務器資源管理器
  • 保存的dbml強制重新編譯
  • 關閉該項目並重新啓動VS服務器連接
  • 重新創建服務器連接並導入PROC

您可能沒有做這些步驟的每一個,但是THA t對我有用。如果你必須使用臨時表,你需要做的是創建一個準系統proc,它只需返回正確的模式,然後在將它導入到OR Designer中之後將其更改爲你想要的。

回答

5

首先 - 重要 - 您的SQL很容易被注入;內部命令應該被參數化:

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

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

此傳遞的@L_author在如在動態指令的@author參數的值 - 防止注入攻擊。


第二 - 你並不真的需要臨時表。它沒有爲你做任何事......你只是插入和選擇。也許只是EXEC,讓結果自然流向調用者?

在其他情況下,表變量會更合適,但這不適用於INSERT/EXEC。


每個調用的列是否相同?如果是這樣,要麼手動寫入dbml,要麼使用臨時SP(僅適用於「WHERE 1 = 0」或其他),以便SET FMT_ONLY ON可以工作。

如果不是(每次使用不同的列),那麼不是一個簡單的答案。也許在這種情況下使用常規的ADO.NET(ExecuteReader/IDataReader - 甚至可能是DataTable.Fill)。

當然,你可以讓LINQ採取應變...(C#):

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

+0

前兩點是這一特定問題的重要,但總的來說,我認爲[SET FMTONLY ON] [http://msdn.microsoft.com/en-us/library/ms173839.aspx]是LINQ to SQL生成的代碼未返回預期結果時問題的核心部分。 在某些情況下,我已將其設置爲OFF [http://www.fishofprey.com/2009/08/detecting-when-nettiers-is.html],當我知道存儲的proc將不會產生任何影響時數據。即只選擇。 – 2010-08-23 09:28:23

5

有沒有真正簡單的方法來做到這一點。我過去也遇到過同樣的問題。我認爲問題在於Linq to Sql無法「計算出」在執行時建立SELECT語句後將返回哪種類型。我做了什麼來解決這個問題,是在存儲過程中,我只做了一個選擇並選擇了我可能需要的所有列。然後,我讓Linq給Sql生成基於此的函數。然後,我又回到了SQL,並將存儲的proc改回了原來的樣子。這裏的技巧不是重新生成DBML。

相關問題