EF4 - 选定的存储过程不返回任何列

Posted

技术标签:

【中文标题】EF4 - 选定的存储过程不返回任何列【英文标题】:EF4 - The selected stored procedure returns no columns 【发布时间】:2011-10-31 00:30:18 【问题描述】:

我在一个存储过程中有查询,它使用一些动态 SQL 调用一些链接服务器。我知道 EF 不喜欢这样,所以我专门列出了所有将返回的列。然而,它仍然不喜欢那样。我在这里做错了什么?我只是希望 EF 能够检测到从存储过程返回的列,以便创建我需要的类。

请查看构成我的存储过程最后几行的以下代码:

SELECT
    #TempMain.ID,
    #TempMain.Class_Data,
    #TempMain.Web_Store_Class1,
    #TempMain.Web_Store_Class2,
    #TempMain.Web_Store_Status,
    #TempMain.Cur_1pc_Cat51_Price,
    #TempMain.Cur_1pc_Cat52_Price,
    #TempMain.Cur_1pc_Cat61_Price,
    #TempMain.Cur_1pc_Cat62_Price,
    #TempMain.Cur_1pc_Cat63_Price,
    #TempMain.Flat_Length,
    #TempMain.Flat_Width,
    #TempMain.Item_Height,
    #TempMain.Item_Weight,
    #TempMain.Um,
    #TempMain.Lead_Time_Code,
    #TempMain.Wp_Image_Nme,
    #TempMain.Wp_Mod_Dte,
    #TempMain.Catalog_Price_Chg_Dt,
    #TempMain.Description,
    #TempMain.Supersede_Ctl,
    #TempMain.Supersede_Pn,
    TempDesc.Cust_Desc,
    TempMfgr.Mfgr_Item_Nbr,
    TempMfgr.Mfgr_Name,
    TempMfgr.Vendor_ID
FROM
    #TempMain
        LEFT JOIN TempDesc ON #TempMain.ID = TempDesc.ID
        LEFT JOIN TempMfgr ON #TempMain.ID = TempMfgr.ID

【问题讨论】:

Why can't Entity Framework see my Stored Procedure's column information?的可能重复 【参考方案1】:

EF 不支持导入从以下位置构建结果集的存储过程:

动态查询 临时表

原因是要导入过程EF必须执行它。这样的操作可能很危险,因为它会触发数据库中的某些更改。因为 EF 在执行存储过程之前使用了特殊的 SQL 命令:

SET FMTONLY ON

通过执行此命令,存储过程将仅返回有关其结果集中列的“元数据”,并且不会执行其逻辑。但是由于没有执行逻辑,因此没有临时表(或构建的动态查询),因此元数据不包含任何内容。

您有两种选择(除了需要重写存储过程以不使用这些功能的一种):

手动定义返回的复杂类型(我想应该可以) 使用 hack 来添加存储过程,并将其放在开头 SET FMTONLY OFF。这将允许您的 SP 的其余代码以正常方式执行。只需确保您的 SP 不会修改任何数据,因为这些修改将在导入期间执行!成功导入后,删除该 hack。

【讨论】:

这对我的一个程序有效,但对另一个无效。当我将过程添加到设计器并在成功添加过程后恢复时,我最终修改了我的过程以返回具有相同数据类型的空结果集。 对我来说关键是“EF必须执行它”,我的proc在使用空参数执行时出错 对于我的情况,它实际上是设置 SET FMTONLY 关闭 "SET FMTONLY OFF" 在大多数情况下也对我有用,但我偶然发现,唯一认为解决问题的方法是消除不必要的临时表并改用 WITH 子句!希望它可以帮助某人。 如果您以正确的方式创建临时表,它支持临时表:声明@MyTemporaryTable TABLE (【参考方案2】:

添加这个非逻辑代码块解决了这个问题。即使它永远不会命中

IF 1=0 BEGIN
    SET FMTONLY OFF
END

Why does my typed dataset not like temporary tables?

http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataset/thread/fe76d511-64a8-436d-9c16-6d09ecf436ea/

【讨论】:

数小时后,这解决了我的问题,tnx 很多 确保在执行此操作后,您的函数实际上可以使用所有 NULL 值执行。 EF 将尝试使用所有 NULL 值实际运行您的查询。做一个跟踪;你会看到的。我们不知道该函数在 NULL 输入上引发整数溢出错误。可能从未意识到这是真正的答案。 如果此解决方案不能解决您的问题,则 EF“获取列信息”向导可能在您的 sp 完成之前超时 - 这也会产生“无列返回结果”。解决方案是通过添加 WHERE 子句来临时加快您的 sp,例如,临时表仅返回有限的数据集。这对我有用。在 EF 检索/保存列信息后,不要忘记删除 WHERE。【参考方案3】:

或者您可以创建一个用户定义的表类型并将其返回。

CREATE TYPE T1 AS TABLE 
( ID bigint NOT NULL
  ,Field1 varchar(max) COLLATE Latin1_General_CI_AI NOT NULL
  ,Field2 bit NOT NULL
  ,Field3 varchar(500) NOT NULL
  );
GO

那么在程序中:

DECLARE @tempTable dbo.T1

INSERT @tempTable (ID, Field1, Field2, Field3)
SELECT .....

....

SELECT * FROM @tempTable

现在 EF 应该能够识别返回的列类型了。

【讨论】:

【参考方案4】:

正如其他人所指出的,请确保该过程实际运行。特别是,就我而言,我在 SQL Server Management Studio 中愉快地运行该过程而没有错误,完全忘记了我是以管理员权限登录的。当我尝试使用我的应用程序的主要用户运行该过程时,我发现查询中有一个该用户无权访问的表。

【讨论】:

【参考方案5】:

有趣的旁注:我首先通过使用表变量而不是临时表(仅用于导入)解决了同样的问题。这对我来说并不是特别直观,并且在最初观察我的两个 SProcs 时让我失望:一个使用临时表,一个使用表变量。

(SET FMTONLY OFF 对我来说从来没有用过,所以我只是暂时更改了我的 SProcs 以获取列信息,而不是像 FYI 一样为 EF 方面的黑客行为而烦恼。)

我最好的选择实际上只是手动创建复杂类型并将函数导入映射到它。效果很好,唯一的区别是设计器中包含了一个额外的 FactoryMethod 来创建属性。

【讨论】:

【参考方案6】:

我要补充的是:

如果存储过程有参数并且没有返回默认参数值的结果集,导入也会失败。

我的存储过程有 2 个浮点参数,当两个参数都为 0 时不会返回任何内容。

所以为了将这个存储过程添加到实体模型中,我在存储过程中设置了这些参数的值,这样就可以保证返回一些行,不管参数实际上是什么。

然后在将此存储过程添加到实体模型后,我撤消了更改。

【讨论】:

另外,你们中的一些人可能正在运行复杂的游标类型存储过程,这些过程需要超过 15 秒才能返回结果,并且数据集表适配器向导会在 15 秒内没有返回任何行后超时。一个简单的解决方法是在存储过程中定位一个复杂的 select 语句,并在其前面加上“top 1”,这样它就会立即返回一行。用它构建你的数据集适配器。并将您的存储过程修改回全选,您应该一切顺利。【参考方案7】:

两种解决方案: 1-手动定义返回的复杂类型(我想它应该可以工作) 2- 使用 hack 并且只是为了添加存储过程放在它的开头 SET FMTONLY OFF。

在某些程序中不与我合作,但它与其他程序合作!

我的程序以这一行结束:

SELECT machineId, production [AProduction]
        , (select production FROM #ShiftBFinalProd WHERE machineId = #ShiftAFinalProd.machineId) [BProduction]
        , (select production FROM #ShiftCFinalProd WHERE machineId = #ShiftAFinalProd.machineId) [CProduction]
     FROM #ShiftAFinalProd
     ORDER BY machineId

谢谢

【讨论】:

【参考方案8】:

除了@tmanthley 所说的之外,请确保您的存储过程实际上可以通过首先在 SSMS 中运行它来运行。我导入了一些存储过程并忘记了几个相关的标量函数,这导致 EF 确定该过程没有返回任何列。看起来我应该早点发现一个错误,但在这种情况下 EF 不会给你错误消息。

【讨论】:

【参考方案9】:

Entity Framework 将尝试通过执行存储过程来获取列,为每个参数传递 NULL。

    请确保存储过程在所有情况下都会返回一些内容。请注意,实体框架使用参数的默认值执行存储过程可能更聪明,而不是 NULL。

    ER 执行以下操作来获取表的元数据:

    打开 FMTONLY

    这会在各种情况下破坏您的存储过程,尤其是在它使用临时表时。

    所以要得到复杂类型的结果;请尝试添加

    设置 FMTONLY 关闭;

这对我有用 - 希望它也对你有用。

转自https://social.msdn.microsoft.com/Forums/en-US/e7f598a2-6827-4b27-a09d-aefe733b48e6/entity-model-add-function-import-stored-procedure-returns-no-columns?forum=adodotnetentityframework

【讨论】:

【参考方案10】:

在我的情况下,在程序顶部添加SET NOCOUNT ON; 可以解决问题。无论如何,这是最佳做法。

【讨论】:

【参考方案11】:

在我的情况下,SET FMTONLY OFF 不起作用。我遵循的方法是,我备份了原始存储过程并仅替换为如下查询的列名。

Select Convert(max,'') as Id,Convert(max,'') as Name

在此更改后,在实体框架中创建新的函数导入,复杂类型。 创建函数导入和复杂类型后,将上述查询替换为您的原始存储过程。

【讨论】:

【参考方案12】:
SET FMTONLY OFF 

在其中一个程序中为我工作,但在其他程序中失败。以下步骤可帮助我解决问题

    在存储过程中,我创建了具有相同列类型的临时表,并将动态查询返回的所有数据插入到临时表中。 并选择临时表数据。

    Create table #temp
    (
       -- columns with same types as dynamic query    
    )
    
    EXEC sp_executeSQL @sql 
    
    insert into #temp 
        Select * from #temp 
    
    drop table #temp
    

    删除了旧存储过程的现有复杂类型、导入函数和存储过程实例,并更新了当前新过程的实体模型。

    在实体模式中为所需的复杂类型编辑导入的函数,您将获得之前存储过程未获得的所有列信息。

    完成类型创建后,您可以从存储过程中删除临时表,然后刷新 Entity Framework。

【讨论】:

【参考方案13】:

在实体框架中,在获取列信息时,sql 执行过程并在参数中传递空值。因此,我通过创建一个包含所有必需列的临时表并在将 null 传递给过程时返回所有没有值的列来以不同的方式处理 null 情况。

在我的过程中有动态查询,比如

declare @category_id    int
set @category_id = (SELECT CATEGORY_ID FROM CORE_USER where USER_ID = @USER_ID)
declare @tableName varchar(15)
declare @sql VARCHAR(max)     
declare  @USER_IDT  varchar(100)    
declare @SESSION_IDT  varchar(10)

 IF (@category_id = 3)     
set @tableName =  'STUD_STUDENT'
else if(@category_id = 4)
set @tableName = 'STUD_GUARDIAN'


if isnull(@tableName,'')<>'' 
begin

set @sql  = 'SELECT  [USER_ID], [FIRST_NAME], SCHOOL_NAME, SOCIETY_NAME, SCHOOL_ID,
SESSION_ID, [START_DATE], [END_DATE]
from  @tableName
....
EXECUTE   (@sql)
END

ELSE
BEGIN
SELECT  * from #UserPrfTemp
END

我没有在 我使用设置 FMTONLY OFF 技巧后的情况。

这是我为获取空白数据而创建的临时表。 现在我正在获取列信息

Create table #UserPrfTemp
(
[USER_ID] bigint, 
[FIRST_NAME] nvarchar(60),
SCHOOL_NAME nvarchar(60),
SOCIETY_NAME nvarchar(200)
.....

【讨论】:

【参考方案14】:

我解决了这个问题,创建了一个表变量,然后从它返回。

DECLARE @VarTable TABLE (
    NeededColumn1                       VARCHAR(100),
    NeededColumn2                       INT,
    NeededColumn3                       VARCHAR(100)
)

...

--Fetch Data from Linked server here

...

INSERT INTO @VarTable (NeededColumn1,NeededColumn2,NeededColumn3)
SELECT Column1, Column2, Column3
FROM #TempTable

SELECT * FROM @VarTable.

这样,您的 SP 结果将绑定到 EF 有权访问的表变量。

【讨论】:

【参考方案15】:

我发现了一种可以帮助大多数人解决正在发生的事情的方法。

调出您最喜欢的 SQL 客户端并运行您尝试更新的 proc,每个参数 = null。当SET FMTONLY ON 时,Visual Studio 确实在尝试这样做。运行跟踪。你会看到的。

您可能会收到错误或意外数据。解决这个问题,您的问题就解决了。

在我的情况下,函数读取 JSON 并失败,因为 JSON 字符串为空。

我只是放了一些类似的东西

IF(@FooJSON IS NULL)
BEGIN
  SELECT 1 VAR1, 2 VAR2;
END
ELSE
--OTHER LOGIC

这可能是一个丑陋的解决方案,但我继承了这个烂摊子,我们不去拉文霍姆。

【讨论】:

以上是关于EF4 - 选定的存储过程不返回任何列的主要内容,如果未能解决你的问题,请参考以下文章

EF 4.1 从存储过程返回结果

实体框架 EF4.1 - 存储过程“在容器中找不到”

为啥调用存储过程不返回任何东西?

调用存储过程不返回任何内容

将DataTable传递给存储过程中的表值参数不起作用[关闭]

存储过程里面set赋值怎么没有起到作用