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

41
假设以下情况:
CREATE PROCEDURE [MySPROC]
AS 
BEGIN

CREATE TABLE #tempSubset(
    [MyPrimaryKey] [bigint]  NOT NULL,
    [OtherColumn]  [int]     NOT NULL)

INSERT INTO #tempSubset (MyPrimaryKey, OtherColumn) 
    SELECT SomePrimaryKey, SomeColumn 
    FROM   SomeHugeTable
    WHERE  LimitingCondition = true

SELECT MyPrimaryKey, OtherColumn 
FROM   #tempSubset
WHERE  SomeExpensiveCondition = true

END

当我生成一个函数导入或映射返回类型时,EF没有生成复杂类型或告诉我:

所选的存储过程或函数未返回任何列

如何解决这个问题?

其他答案建议使用表变量(出于性能原因不会这样做)伪造返回模式并注释掉真实的存储过程,还有人建议用视图类似地处理...但一定有一种方法可以在不增加不必要开销或要求我破坏存储过程以更新模型的情况下完成此操作吗?

4个回答

66
CREATE PROCEDURE [MySPROC]
AS 
BEGIN

--supplying a data contract
IF 1 = 2 BEGIN
    SELECT
        cast(null as bigint)  as MyPrimaryKey,
        cast(null as int)    as OtherColumn
    WHERE
        1 = 2  
END

CREATE TABLE #tempSubset(
    [MyPrimaryKey] [bigint]  NOT NULL,
    [OtherColumn]  [int]     NOT NULL)

INSERT INTO #tempSubset (MyPrimaryKey, OtherColumn) 
    SELECT SomePrimaryKey, SomeColumn 
    FROM   SomeHugeTable
    WHERE  LimitingCondition = true

SELECT MyPrimaryKey, OtherColumn 
FROM   #tempSubset
WHERE  SomeExpensiveCondition = true

END

提供一个虚假的数据契约来处理结果集是最简单、最清晰和最快的方法。在SSIS的数据源控件中也存在同样的问题。.NET将从查询的不可访问的“合约”部分读取结果集,并为复杂类型提供元数据。没有性能影响,也无需注释执行实际工作的SQL语句。


工作得非常好,比那些替代品好太多了!谢谢 ;) - JoeBrockhaus
4
请注意,在您的模型中生成复杂类型后,按照此格式提供数据契约后,属性将是可空的,即“public Nullable<int> MyProperty {get; set;}”。这是可以接受的,因为它将处理数据库列值为空的情况。如果您执行类似于“SELECT 1 AS [MyProperty column]”的操作,则属性将变为“public int MyProperty { get; set; }”(您必须实际删除[sproc]_Result类+复杂类型并重新生成复杂类型,以便更改底层sProc的更改得到识别)。根据您的情况选择其中一种方法即可,只需注意这点。 - SleepyBoBos
@SleepyBoBos,不确定是否有所改变,但我最近进行了像你说的更改,并能够使用函数导入属性窗口的更新功能来获取 Null vs Not Null 中的更改。 - JoeBrockhaus
1
嗨,SleepyBobos和BrianKrahenbuhl,非常感谢你们的解决方案。它对我很有用。 - t4thilina
1
我想知道答案中的 trEND 是否应该是 true - Pang
显示剩余3条评论

57

在存储过程定义的顶部添加以下内容:

SET FMTONLY OFF
允许模型从临时表中推断模式而不出问题。此外,这不需要为合同进行额外的维护。

例子:

SET FMTONLY OFF

CREATE TABLE #tempTable (
    ...
)

...

SELECT * FROM #tempTable 

1
迄今为止最简单和最快的解决方案。谢谢。 - Xipooo
谢谢。更加简洁的解决方案。 - Ashby
理想的解决方案。不需要像所选答案中那样创建虚拟查询。 - Zafar
非常简单的解决方案。谢谢,- Xipooo - Gobind Gupta
它能够工作,但在微软网站上显示: 注意 - 不要使用此功能。该功能已被以下项目所取代: https://learn.microsoft.com/en-us/sql/t-sql/statements/set-fmtonly-transact-sql?view=sql-server-ver16 - Abhi

12

解决方案1:使用表变量替代临时表。

解决方案2:在存储过程中使用 “Set FMTONLY off;” SQL 命令,您将获得创建新复合类型所需的列信息。

解决方案3:虽然这不是一个好方法,但这是一种非常简单的方法。只需添加具有虚拟数据的 select 语句即可,因为 1=0,所以不会执行。

更多详细信息请查看此链接


1
我已经尝试了所有3种方法。它们都没有起作用。由于我总是晚了2天,而且我已经花费了2个小时在这上面。我需要继续前进。我的临时解决方案是查看模型细节以更改复合类型(查找过程和变量),并手动从int32更改为string。这是一个坏主意。如果刷新,它会消失。我甚至不能将其记录下来以提醒其他开发人员。任何人发现发生了什么,请让每个人知道。我正在使用VS 2015,EF 6,SQL Server 2016。谢谢。 - user12345

0

这是不完整的,但当设置 fmtonly off 无效时,您可以使用以下方法生成数据契约:

        SELECT * 
        FROM tempdb.sys.columns 
        WHERE [object_id] = OBJECT_ID(N'tempdb..#u');

        select case  system_type_id 
        when 62 then 'cast(null as float) as ' 
        when 175 then 'cast(null as char(' + cast(max_length as varchar(50)) + ')) as ' 
        when 167 then 'cast(null as varchar(' + cast(max_length as varchar(50)) + ')) as ' 
        when 56 then 'cast(null as int) as ' 
        when 104 then 'cast(null as bit) as ' 
        when 106 then 'cast(null as decimal(' + cast(precision as varchar(50)) + ',' + cast(scale as varchar(50)) + ')) as ' 
        when 40 then 'cast(null as date) as '            
        end
        + name + ','
        from  tempdb.sys.columns 
        WHERE [object_id] = OBJECT_ID(N'tempdb..#u');

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接