我在一个存储过程中查询,该存储过程使用一些动态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
答案 0 :(得分:142)
EF不支持导入构建结果集的存储过程:
原因是导入程序 EF必须执行。此类操作可能很危险,因为它可能会触发数据库中的某些更改。因为EF在执行存储过程之前使用特殊的SQL命令:
SET FMTONLY ON
通过执行此命令,存储过程将仅返回有关其结果集中列的“元数据”,并且不会执行其逻辑。但由于逻辑没有执行,因此没有临时表(或构建动态查询),因此元数据不包含任何内容。
您有两个选择(除了需要重写存储过程以不使用这些功能的选项):
SET FMTONLY OFF
的存储过程。这将允许您的SP代码的其余部分以正常方式执行。只需确保您的SP不会修改任何数据,因为这些修改将在导入期间执行!导入成功后删除该黑客。答案 1 :(得分:26)
添加此非逻辑代码块可解决此问题。即使它永远不会命中
IF 1=0 BEGIN
SET FMTONLY OFF
END
答案 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
为我的其中一个程序工作,但其他程序失败了。以下步骤可帮助我解决我的问题
在存储过程中,我创建了具有相同列类型的临时表,并将动态查询返回的所有数据都插入到临时表中。 并选择临时表数据。
Create table #temp
(
-- columns with same types as dynamic query
)
EXEC sp_executeSQL @sql
insert into #temp
Select * from #temp
drop table #temp
删除旧存储过程的现有复杂类型,导入函数和存储过程实例以及当前新过程的更新实体模型。
在实体模式中编辑导入的函数以获取所需的复杂类型,您将获得那些以前存储过程无法获得的列信息。
完成类型创建后,可以从存储过程中删除临时表,然后刷新实体框架。
答案 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。
请确保存储过程在所有情况下都会返回。请注意,实体框架可能更聪明地使用参数的默认值执行存储过程,而不是NULL。
ER执行以下操作以获取表格的元数据:
设置FMTONLY
这将在各种情况下破坏您的存储过程,特别是如果它使用临时表。
所以要得到一个复杂类型的结果;请尝试添加
设置FMTONLY OFF;
这对我有用 - 希望它也适合你。
答案 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。