(附加)EF无法从#temp表中选择的存储过程推断返回模式

我有类似的问题

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 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中使用。

这是我成功工作的一个例子。

 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)“添加function导入”成功创建

a)sp_SoInfoDocs函数导入

b)sp_SoInfoDocs复杂类型

我现在可以成功调用我的存储过程,如下所示

  using (TestEntities context = new TestEntities()) { string searchText = "rem"; ObjectParameter total = new ObjectParameter("out",typeof(int)); List 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表中的元数据。

如果其他人遇到类似的问题而且我没有完全解释为什么我采用了这个解决方案以及我如何使它工作〜那么请随时告诉我,我会尽力解释。

为什么要使用临时表或表变量。 表变量有许多性能缺点,如:

表变量没有分布统计信息,它们不会触发重新编译 。 因此,在许多情况下,优化器将在假设表变量没有行的情况下构建查询计划。 因此,如果您期望有更多行(大于100),则应谨慎使用表变量。 在这种情况下,临时表可能是更好的解决方案。 或者,对于将表变量与其他表连接的查询,请使用RECOMPILE提示,这将使优化器对表变量使用正确的cardinatlity。

SQL Server优化器的基于开销的推理模型不支持表变量。 因此,当需要基于成本的选择来实现有效的查询计划时,不应使用它们。 当需要基于成本的选择时,首选临时表。 这通常包括具有连接,并行性决策和索引选择选择的查询。

修改表变量的查询不会生成并行查询执行计划。 当修改非常大的表变量或复杂查询中的表变量时,性能会受到影响。 在这些情况下,请考虑使用临时表。 有关更多信息,请参见CREATE TABLE(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