[英](Additional) EF can't infer return schema from stored procedure selecting from a #temp table
我有一個類似的問題
並且我已經根據上述解決方案創建了我的存儲過程解決方案,但我仍然遇到類似的 EF 錯誤,我真的不知道為什么或不知道如何修復它。
'rowNum' 類型的成員在數據讀取器中沒有對應的同名列。
我的具體錯誤:
數據讀取器與指定的“TestModel.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
我的總體目標是:
搜索聚集索引表 (docInfo) 並查找包含特定搜索字符串值的所有行
同時從與每個 docInfo 對象關聯的其他表中捕獲元數據
上面的操作 (1) 和 (2) 的結果被寫入一個 #temp 表,然后我添加一個 rowNum 列,使我能夠引入分頁,即
根據提供的 PageNumber 和 PageSize 變量,對可以在任何時間返回的元數據結果數量進行分頁。
什么工作
我能夠成功創建存儲過程。
在 SSMS 中,我能夠成功執行存儲過程並提供我期望的結果,這是一個示例
在 EF 中,我已經能夠通過從數據庫更新來更新和導入存儲過程
在 EF 中,我可以看到函數導入並可以看到映射
在 ED 中,我可以看到生成的復雜類型
我使用下面的代碼來調用進程
using (TestEntities context = new TestEntities()) { List<sp_SoInfoDocs_Result> lst = context.sp_SoInfoDocs(searchText, 1, 10).ToList(); }
我編譯並運行我的解決方案並從 EF 收到以下錯誤
EntityFramework.SqlServer.dll 中發生“System.Data.Entity.Core.EntityCommandExecutionException”
附加信息:數據讀取器與指定的“SysviewModel.sp_SoInfoDocs_Result”不兼容。 'rowNum' 類型的成員在數據讀取器中沒有對應的同名列。
我是 SSMS/SQL 和 EF 的新手/基本用戶,據我所知/可以去,這已經讓我感到很困惑,我真的不知道下一步該去哪里解決這個問題.
我已經通過 SO 進行了廣泛的搜索,可以看到其他遇到類似問題並嘗試過建議的解決方案的人,但似乎對我沒有任何作用。
我真的會非常非常感謝任何能幫助我理解的人
有什么不對的/我做錯了什么?
有沒有更好的方法來實現我的需要?
關於如何解決這個問題的想法。
提前致謝
我找到的解決方案是使用 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.