簡體   English   中英

(附加)EF 無法從#temp 表中選擇的存儲過程推斷返回模式

[英](Additional) EF can't infer return schema from stored procedure selecting from a #temp table

我有一個類似的問題

EF 無法從 #temp 表中選擇的存儲過程推斷返回架構

並且我已經根據上述解決方案創建了我的存儲過程解決方案,但我仍然遇到類似的 EF 錯誤,我真的不知道為什么或不知道如何修復它。

'rowNum' 類型的成員在數據讀取器中沒有對應的同名列。

我的具體錯誤:

數據讀取器與指定的“Te​​stModel.sp_SoInfoDocs_Result”不兼容。 'rowNum' 類型的成員在數據讀取器中沒有對應的同名列。

我的存儲過程:

ALTER PROCEDURE [dbo].[sp_SoInfoDocs] 
    @searchText nvarchar(200),
    @PageNumber int,
    @PageSize int
AS
BEGIN
    IF 1 = 2  
    BEGIN    
       SELECT
          cast(null as int )  as rowNum
          ,cast(null as text)    as serverName
          ,cast(null as text)    as jobName
          ,cast(null as DATETIME)    as oDate
          ,cast(null as int)    as runCount
          ,cast(null as nvarchar(10))    as orderID
          ,cast(null as text)    as applicationName
          ,cast(null as text)    as memberName
          ,cast(null as text)    as nodeID
          ,cast(null as nvarchar(10))    as endStatus
          ,cast(null as int)    as returnCode
          ,cast(null as DATETIME)    as startTime
          ,cast(null as DATETIME)    as endTime
          ,cast(null as nvarchar(50))    as status
          ,cast(null as text)    as owner
          ,cast(null as bit)    as existsNote
      WHERE
          1 = 2  
   END

DECLARE @LowerLimit int;
SET @LowerLimit = (@PageNumber - 1) * @PageSize;
DECLARE @UpperLimit int; 
SET @UpperLimit = @PageNumber * @PageSize;

PRINT CAST (@LowerLimit as varchar)  
PRINT CAST (@UpperLimit as varchar)

SELECT ROW_NUMBER() over (order by  Expr1) as rowNum, * 
into #temp
from ( 

SELECT     dbo.SOInfo.jobName, dbo.SOInfo.nodeID, dbo.SOInfo.nodeGroup, dbo.SOInfo.endStatus, dbo.SOInfo.returnCode, dbo.SOInfo.startTime, dbo.SOInfo.endTime, 
                      dbo.SOInfo.oDate, dbo.SOInfo.orderID, dbo.SOInfo.status, dbo.SOInfo.runCount, dbo.SOInfo.owner, dbo.SOInfo.cyclic, dbo.SOInfo.soInfoID, dbo.SOInfo.docInfoID, 
                      dbo.SOInfo.existsNote, dbo.SOInfo.noSysout, dbo.serverInfo.serverName, dbo.Groups.label AS applicationName, Groups_1.label AS memberName, 
                      Groups_2.label AS groupName, Groups_3.label AS scheduleTableName, dbo.SOInfo.serverInfoID, dbo.SOInfo.applicationID, dbo.SOInfo.groupID, 
                      dbo.SOInfo.memberID, dbo.SOInfo.scheduleTableID, dbo.docFile.docFileID, dbo.docInfo.docInfoID AS Expr1, dbo.docFile.docFileObject
FROM         dbo.SOInfo INNER JOIN
                      dbo.serverInfo ON dbo.SOInfo.serverInfoID = dbo.serverInfo.serverInfoID INNER JOIN
                      dbo.docInfo ON dbo.SOInfo.docInfoID = dbo.docInfo.docInfoID INNER JOIN
                      dbo.docFile ON dbo.docInfo.docFileID = dbo.docFile.docFileID LEFT OUTER JOIN
                      dbo.Groups AS Groups_3 ON dbo.SOInfo.scheduleTableID = Groups_3.ID LEFT OUTER JOIN
                      dbo.Groups AS Groups_1 ON dbo.SOInfo.memberID = Groups_1.ID LEFT OUTER JOIN
                      dbo.Groups AS Groups_2 ON dbo.SOInfo.groupID = Groups_2.ID LEFT OUTER JOIN
                      dbo.Groups ON dbo.SOInfo.applicationID = dbo.Groups.ID

                      WHERE CONTAINS (docfileObject,@searchText) 
) tbl

  SELECT Count(1) FROM #temp

  SELECT rowNum, serverName, jobName ,oDate,runCount,orderID,applicationName,memberName,nodeID, endStatus, returnCode,startTime,endTime,status,owner,existsNote      
  FROM #temp WHERE rowNum > @LowerLimit AND rowNum <= @UpperLimit 
END

我的總體目標是:

  1. 搜索聚集索引表 (docInfo) 並查找包含特定搜索字符串值的所有行

  2. 同時從與每個 docInfo 對象關聯的其他表中捕獲元數據

  3. 上面的操作 (1) 和 (2) 的結果被寫入一個 #temp 表,然后我添加一個 rowNum 列,使我能夠引入分頁,即

  4. 根據提供的 PageNumber 和 PageSize 變量,對可以在任何時間返回的元數據結果數量進行分頁。

什么工作

  1. 我能夠成功創建存儲過程。

  2. 在 SSMS 中,我能夠成功執行存儲過程並提供我期望的結果,這是一個示例

    在此處輸入圖片說明

  3. 在 EF 中,我已經能夠通過從數據庫更新來更新和導入存儲過程

    在此處輸入圖片說明

  4. 在 EF 中,我可以看到函數導入並可以看到映射

    在此處輸入圖片說明

    在此處輸入圖片說明

  5. 在 ED 中,我可以看到生成的復雜類型

    在此處輸入圖片說明

  6. 我使用下面的代碼來調用進程

    using (TestEntities context = new TestEntities()) { List<sp_SoInfoDocs_Result> lst = context.sp_SoInfoDocs(searchText, 1, 10).ToList(); }
  7. 我編譯並運行我的解決方案並從 EF 收到以下錯誤

    EntityFramework.SqlServer.dll 中發生“System.Data.Entity.Core.EntityCommandExecutionException”

    附加信息:數據讀取器與指定的“SysviewModel.sp_SoInfoDocs_Result”不兼容。 'rowNum' 類型的成員在數據讀取器中沒有對應的同名列。

我是 SSMS/SQL 和 EF 的新手/基本用戶,據我所知/可以去,這已經讓我感到很困惑,我真的不知道下一步該去哪里解決這個問題.

我已經通過 SO 進行了廣泛的搜索,可以看到其他遇到類似問題並嘗試過建議的解決方案的人,但似乎對我沒有任何作用。

我真的會非常非常感謝任何能幫助我理解的人

  1. 有什么不對的/我做錯了什么?

  2. 有沒有更好的方法來實現我的需要?

  3. 關於如何解決這個問題的想法。

提前致謝

我找到的解決方案是使用 SQL 臨時變量而不是使用臨時表,這使我能夠通過我的最終 SQL Select 語句公開表列,然后使用“添加函數導入”函數將它們作為 EF 中的元數據使用。

這是我成功工作的 sp 的示例。

USE [TestDB]
GO
/****** Object:  StoredProcedure [dbo].[sp_SoInfoDocs_Archive]    Script Date: 09/07/2015 10:35:43 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[sp_SoInfoDocs_Archive] 
    @searchText nvarchar(200),
    @PageNumber int,
    @PageSize int,
    @out int = 0 output

AS
BEGIN

DECLARE @LowerLimit int;
SET @LowerLimit = (@PageNumber - 1) * @PageSize;
DECLARE @UpperLimit int; 
SET @UpperLimit = @PageNumber * @PageSize;

-- Create temporary column variables

Declare @temp TABLE  
(
rowNum INT,
jobName text, 
nodeID nvarchar(50),
nodeGroup text,
endStatus nvarchar(10),
returnCode int,
startTime datetime,
endTime datetime,
oDate smalldatetime, 
orderID nvarchar(10),
status nvarchar(50),
runCount int,
owner text,
cyclic text,
soInfoID int,
docInfoID int,
existsNote bit,
noSysout bit,
serverName varchar(256),
applicationName nvarchar(255),
memberName nvarchar(255), 
groupName nvarchar(255),
scheduleTableName nvarchar(255),
serverInfoID int,
applicationID int,
groupID int, 
memberID int,
scheduleTableID int,
docFileID int,
Expr1 int,
docFileObject varbinary(MAX)
)

INSERT INTO @temp 
SELECT ROW_NUMBER() over (order by  Expr1) as rowNum, * 
from ( 

SELECT     dbo.SOInfoArchive.jobName, 
dbo.SOInfoArchive.nodeID,
dbo.SOInfoArchive.nodeGroup,
dbo.SOInfoArchive.endStatus,
dbo.SOInfoArchive.returnCode,
dbo.SOInfoArchive.startTime,
dbo.SOInfoArchive.endTime,
dbo.SOInfoArchive.oDate, 
dbo.SOInfoArchive.orderID,
dbo.SOInfoArchive.status,
dbo.SOInfoArchive.runCount,
dbo.SOInfoArchive.owner,
dbo.SOInfoArchive.cyclic,
dbo.SOInfoArchive.soInfoID,
dbo.SOInfoArchive.docInfoID,
dbo.SOInfoArchive.existsNote,
dbo.SOInfoArchive.noSysout,
dbo.serverInfo.serverName,
dbo.Groups.label AS applicationName,
Groups_1.label AS memberName, 
Groups_2.label AS groupName,
Groups_3.label AS scheduleTableName,
dbo.SOInfoArchive.serverInfoID,
dbo.SOInfoArchive.applicationID,
dbo.SOInfoArchive.groupID, 
dbo.SOInfoArchive.memberID,
dbo.SOInfoArchive.scheduleTableID,
dbo.docFile.docFileID,
dbo.docInfo.docInfoID AS Expr1,
dbo.docFile.docFileObject
FROM         dbo.SOInfoArchive INNER JOIN
                      dbo.serverInfo ON dbo.SOInfoArchive.serverInfoID = dbo.serverInfo.serverInfoID INNER JOIN
                      dbo.docInfo ON dbo.SOInfoArchive.docInfoID = dbo.docInfo.docInfoID INNER JOIN
                      dbo.docFile ON dbo.docInfo.docFileID = dbo.docFile.docFileID LEFT OUTER JOIN
                      dbo.Groups AS Groups_3 ON dbo.SOInfoArchive.scheduleTableID = Groups_3.ID LEFT OUTER JOIN
                      dbo.Groups AS Groups_1 ON dbo.SOInfoArchive.memberID = Groups_1.ID LEFT OUTER JOIN
                      dbo.Groups AS Groups_2 ON dbo.SOInfoArchive.groupID = Groups_2.ID LEFT OUTER JOIN
                      dbo.Groups ON dbo.SOInfoArchive.applicationID = dbo.Groups.ID

                      WHERE CONTAINS (docfileObject,@searchText) 
) tbl

-- Select enables me to consume the following columns as meta data in EF

  SELECT rowNum,
         serverName,
         jobName ,
          oDate,
          runCount,
          orderID,
          applicationName,
          memberName,
          nodeID,
          endStatus,
          returnCode,
          startTime,
          endTime,
          status,
          owner,
          existsNote,
          docFileID      
  FROM @temp WHERE rowNum > @LowerLimit AND rowNum <= @UpperLimit 



END

總結一下,我現在可以

  • 1) 將存儲過程導入我的 EDMX。

  • 2)“添加函數導入”成功創建

a) sp_SoInfoDocs 函數導入

b) sp_SoInfoDocs 復雜類型

我現在可以按如下方式成功調用我的存儲過程

     using (TestEntities context = new TestEntities())
    {
          string searchText = "rem";
          ObjectParameter total = new ObjectParameter("out",typeof(int));

          List<sp_SoInfoDocs_Result> lst = context.sp_SoInfoDocs(searchText, 1, 10, total).ToList();

          foreach (var item in lst)
          {
            Console.WriteLine(item.jobName + " " + item.oDate + " " + item.serverName + " " + item.startTime + " " + item.endTime);

          }
           Console.ReadLine();
    } 

以及返回結果的示例。

在此處輸入圖片說明

我現在成功地使用此過程的基礎在我的視圖中導入和顯示動態創建的 HTML 表中的元數據。

如果其他人遇到類似的問題,而我忽略了完全解釋我為什么采用此解決方案以及我如何使其工作〜那么請隨時 PM 我,我會盡力解釋。

為什么要使用臨時表或表變量。 表變量有許多性能缺陷,例如:

表變量沒有分布統計信息,它們不會觸發重新編譯 因此,在許多情況下,優化器會在假設表變量沒有行的情況下構建查詢計划。 出於這個原因,如果您希望有更多行(大於 100),則應謹慎使用表變量。 在這種情況下,臨時表可能是更好的解決方案。 或者,對於將表變量與其他表連接的查詢,請使用 RECOMPILE 提示,這將導致優化器為表變量使用正確的基數。

SQL Server 優化器的基於成本的推理模型不支持表變量。 因此,當需要基於成本的選擇來實現高效的查詢計划時,不應使用它們。 當需要基於成本的選擇時,首選臨時表。 這通常包括帶有連接的查詢、並行決策和索引選擇選項。

修改表變量的查詢不會生成並行查詢執行計划。 當修改非常大的表變量或復雜查詢中的表變量時,性能可能會受到影響。 在這些情況下,請考慮改用臨時表。 有關詳細信息,請參閱創建表 (Transact-SQL) 讀取表變量而不修改它們的查詢仍然可以並行化。

使用簡單的 CTE:

ALTER PROCEDURE [dbo].[sp_SoInfoDocs_Archive] 
    @searchText NVARCHAR(200),
    @PageNumber INT,
    @PageSize   INT,
    @out        INT = 0 OUTPUT

AS
BEGIN
SET NOCOUNT ON;
DECLARE @LowerLimit INT = (@PageNumber - 1) * @PageSize;
DECLARE @UpperLimit INT = @PageNumber * @PageSize;


;WITH cte AS
(
  SELECT
    dbo.SOInfoArchive.jobName, 
    dbo.SOInfoArchive.nodeID,
    dbo.SOInfoArchive.nodeGroup,
    dbo.SOInfoArchive.endStatus,
    dbo.SOInfoArchive.returnCode,
    dbo.SOInfoArchive.startTime,
    dbo.SOInfoArchive.endTime,
    dbo.SOInfoArchive.oDate, 
    dbo.SOInfoArchive.orderID,
    dbo.SOInfoArchive.status,
    dbo.SOInfoArchive.runCount,
    dbo.SOInfoArchive.owner,
    dbo.SOInfoArchive.cyclic,
    dbo.SOInfoArchive.soInfoID,
    dbo.SOInfoArchive.docInfoID,
    dbo.SOInfoArchive.existsNote,
    dbo.SOInfoArchive.noSysout,
    dbo.serverInfo.serverName,
    dbo.Groups.label AS applicationName,
    Groups_1.label AS memberName, 
    Groups_2.label AS groupName,
    Groups_3.label AS scheduleTableName,
    dbo.SOInfoArchive.serverInfoID,
    dbo.SOInfoArchive.applicationID,
    dbo.SOInfoArchive.groupID, 
    dbo.SOInfoArchive.memberID,
    dbo.SOInfoArchive.scheduleTableID,
    dbo.docFile.docFileID,
    dbo.docInfo.docInfoID AS Expr1,
    dbo.docFile.docFileObject
  FROM dbo.SOInfoArchive
  JOIN dbo.serverInfo 
    ON dbo.SOInfoArchive.serverInfoID = dbo.serverInfo.serverInfoID
  JOIN dbo.docInfo 
   ON dbo.SOInfoArchive.docInfoID = dbo.docInfo.docInfoID
  JOIN dbo.docFile 
    ON dbo.docInfo.docFileID = dbo.docFile.docFileID
  LEFT JOIN dbo.Groups AS Groups_3 
    ON dbo.SOInfoArchive.scheduleTableID = Groups_3.ID
  LEFT JOIN dbo.Groups AS Groups_1 
    ON dbo.SOInfoArchive.memberID = Groups_1.ID
  LEFT JOIN dbo.Groups AS Groups_2 
    ON dbo.SOInfoArchive.groupID = Groups_2.ID
  LEFT JOIN dbo.Groups 
    ON dbo.SOInfoArchive.applicationID = dbo.Groups.ID
  WHERE CONTAINS (docfileObject,@searchText) 
),
 cte2 AS 
(
   SELECT ROW_NUMBER() OVER (ORDER BY  Expr1) AS rowNum, * 
   FROM cte
)
SELECT rowNum,
       serverName,
       jobName ,
       oDate,
       runCount,
       orderID,
       applicationName,
       memberName,
       nodeID,
       endStatus,
       returnCode,
       startTime,
       endTime,
       status,
       owner,
       existsNote,
       docFileID      
  FROM cte2 
  WHERE rowNum > @LowerLimit
    AND rowNum <= @UpperLimit 
END

可能沒有人再關心了,但它不起作用的原因是 ROW_NUMBER() 返回一個 BIGINT 並且您的代碼以這種方式定義結構: cast(null as int) as rowNum

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM