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

Posted

技术标签:

【中文标题】EF 无法从 #temp 表中选择的存储过程推断返回模式【英文标题】:EF can't infer return schema from Stored Procedure selecting from a #temp table 【发布时间】:2013-05-11 16:52:00 【问题描述】:

假设如下:

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 不会生成复杂类型或告诉我:

选定的存储过程或函数不返回任何列

如何克服这个问题?

其他答案建议using table variables(出于性能原因不会这样做)faking the return schema and commenting out the real stored procedure,其他建议doing similar with views...但必须有一种方法可以做到这一点,而不必增加不必要的开销或要求我这样做打破存储过程来更新模型?

【问题讨论】:

【参考方案1】:
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。

【讨论】:

效果很好,比那些替代品好得多!谢谢;) 已经过测试,效果很好!将添加到我用来生成 CREATE PROCEDURES 的脚本中 请注意,如果您在模型中生成复杂类型,在提供此格式的数据协定后,属性将为空,即 'public Nullable MyProperty get;放;)'。这很好,因为它将处理 db 列值为空的情况。如果您在合同中执行类似“SELECT 1 AS [MyProperty column]”的操作,则该属性将为“public int MyProperty get;放; '(您必须实际删除 [sproc]_Result 类 + 复杂类型和 regen 复杂类型,才能看到底层 sProc 的变化)。无论是还是取决于您的情况,请注意。 嘿 SleepyBobos 和 BrianKrahenbuhl 非常感谢您的解决方案。它对我有用。 我想知道答案中的trEND是否应该是true【参考方案2】:

将此添加到存储过程定义的顶部:

SET FMTONLY OFF
允许模型从临时表中推断模式而不会出现问题。作为奖励,它不需要额外的合同维护。

示例:

SET FMTONLY OFF

CREATE TABLE #tempTable (
    ...
)

...

SELECT * FROM #tempTable 

【讨论】:

绝对是最简单最快的解决方案。谢谢。 谢谢。更清洁的解决方案。 理想的解决方案。无需像所选答案那样创建虚拟查询 非常简单的灵魂。谢谢,-Xipooo【参考方案3】:

解决方案 1 使用表变量而不是临时表。

解决方案 2 使用设置 FMTONLY 关闭;过程中的 SQL 命令,您将获得创建新复杂类型的列信息。

解决方案 3 这不是一个好方法,但它是一个非常简单的方法。只需添加一个带有虚拟数据的 select 语句,它不会执行,因为 1=0。

您可以在this link查看详细信息

【讨论】:

我已经尝试了所有 3 种方法。他们都没有工作。因为我总是迟到 2 天,我为此花了 2 个小时。我需要继续前进。我的临时解决方案是查看模型详细信息以更改复合类型(查找过程和变量)并手动从 int32 更改为 string。这是个坏主意。如果刷新,它将丢失。我什至无法记录它以提醒其他开发人员。任何人都知道会发生什么。请让大家知道。我正在使用 VS 2015、EF 6、SQL Server 2016。TX【参考方案4】:

这是不完整的,但是当设置 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');

【讨论】:

以上是关于EF 无法从 #temp 表中选择的存储过程推断返回模式的主要内容,如果未能解决你的问题,请参考以下文章

Postgresql存储过程select into temp table。

Oracle 中临时表的替代方案

必须声明表变量“@temp”

EF工作流程

SQL Server 存储过程创建临时表并插入值

无法执行 FunctionImport“entitys.xx”,因为未将它映射到存储函数。EF