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

时间:2011-08-20 00:35:49

标签: sql-server entity-framework-4 temp-tables

我在一个存储过程中查询,该存储过程使用一些动态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

15 个答案:

答案 0 :(得分:142)

EF不支持导入构建结果集的存储过程:

  • 动态查询
  • 临时表

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

SET FMTONLY ON

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

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

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

答案 1 :(得分:26)

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

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/

答案 2 :(得分:12)

或者您可以创建用户定义的表类型并返回该表。

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应该能够识别返回的列类型。

答案 3 :(得分:2)

正如其他人所说,确保程序实际运行。特别是在我的情况下,我在SQL Server Management Studio中没有错误地运行该过程,完全忘记了我使用管理员权限登录。一旦我尝试使用我的应用程序的主要用户运行该过程,我发现查询中有一个表,该用户没有访问权限。

答案 4 :(得分:1)

我要补充的是:

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

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

因此,为了将此存储过程添加到实体模型,我在存储过程中设置这些参数的值,以便保证返回某些行,无论参数实际是什么。

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

答案 5 :(得分:1)

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

(SET FMTONLY OFF从来没有为我工作过,所以我只是暂时更改我的SProcs以获取列信息,而不是像在FYI上那样费心去攻击EF端。)

我最好的选择实际上只是手动创建复杂类型并将函数导入映射到它。工作得很好,唯一的区别就是设计师中包含了另外一个用于创建属性的FactoryMethod。

答案 6 :(得分:1)

两种解决方案: 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

由于

答案 7 :(得分:1)

除了@tmanthley所说的内容之外,请确保您的存储过程实际上是通过在SSMS中首先运行它来实现的。我导入了一些存储过程并忘记了几个依赖的标量函数,这导致EF确定该过程没有返回任何列。看起来像我之前应该抓到的一个错误,但在这种情况下EF并没有给你一个错误信息。

答案 8 :(得分:0)

在我的案例中,在程序的顶部添加SET NOCOUNT ON;修复了问题。无论如何,这是最好的做法。

答案 9 :(得分:0)

在我的情况下,SET FMTONLY OFF无效。我遵循的方法是,我采用了原始存储过程的备份,只用下面的查询替换了列名。

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

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

答案 10 :(得分:0)

SET FMTONLY OFF 

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

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

    Create table #temp
    (
       -- columns with same types as dynamic query    
    )
    
    EXEC sp_executeSQL @sql 
    
    insert into #temp 
        Select * from #temp 
    
    drop table #temp
    
  2. 删除旧存储过程的现有复杂类型,导入函数和存储过程实例以及当前新过程的更新实体模型。

  3. 在实体模式中编辑导入的函数以获取所需的复杂类型,您将获得那些以前存储过程无法获得的列信息。

  4. 完成类型创建后,可以从存储过程中删除临时表,然后刷新实体框架。

答案 11 :(得分:0)

在Entity框架中,在获取列信息时,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)
.....
}

答案 12 :(得分:0)

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

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

  2. ER执行以下操作以获取表格的元数据:

    设置FMTONLY

  3. 这将在各种情况下破坏您的存储过程,特别是如果它使用临时表。

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

    设置FMTONLY OFF;

  5. 这对我有用 - 希望它也适合你。

    摘自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

答案 13 :(得分:0)

我解决了创建表变量然后从中返回的问题。

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可以访问的表变量。

答案 14 :(得分:0)

我发现了一种可以帮助大多数人摆脱困境的方法。

拉起您喜欢的SQL客户端,并使用每个参数= null运行您要更新的proc。 Visual Studio实际上在SET FMTONLY ON时尝试执行此操作。运行跟踪。您会看到的。

您可能会收到错误消息或意外数据。解决此问题,您的问题已解决。

就我而言,该函数以JSON格式读取并失败,因为JSON字符串为空。

我只是把类似的东西

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

这可能是一个丑陋的解决方案,但是我继承了这个烂摊子,因此我们不涉及Ravenholm。