提问人:Rod 提问时间:8/15/2013 最后编辑:Rod 更新时间:5/14/2020 访问量:58260
存储过程返回 int 而不是结果集
Stored procedure returns int instead of result set
问:
我有一个包含动态选择的存储过程。像这样的东西:
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--@p1 int = 0,
--@p2 int = 0
@groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE @query NVARCHAR(max)
SET @query = 'SELECT * FROM CUSTOMERS WHERE Id = ' + @groupId
/* This actually contains a dynamic pivot select statement */
EXECUTE(@query);
END
在 SSMS 中,存储过程运行良好并显示结果集。
在使用实体框架的 C# 中,它显示返回一个而不是?int
IEnumerable
private void LoadTestRecords()
{
TestRecordsDBEntities dataContext = new TestRecordsDBEntities();
string id = ddlGroupId.SelectedValue;
List<TestRecord> list = dataContext.usp_GetTestRecords(id); //This part doesn't work returns int
GridView1.DataSource = list;
}
生成的函数usp_GetTestRecords
public virtual int usp_GetTestRecords(string groupId)
{
var groupIdParameter = groupId != null ?
new ObjectParameter("groupId", groupId) :
new ObjectParameter("groupId", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("usp_GetTestRecords", groupIdParameter);
}
答:
在为存储过程生成模型类时,错误地选择了标量返回结果。应从实体模型中删除存储过程,然后重新添加存储过程。在存储过程的对话框中,可以选择所需的返回类型。不要只是编辑生成的代码。这现在可能有效,但如果对模型进行其他更改,则可以替换生成的代码。
评论
实体框架无法判断存储过程返回的内容。我成功地创建了一个表变量,该变量反映了 SELECT 语句中的数据。只需插入到表变量中,然后从该表变量中进行选择即可。EF 应该捡起它。
评论
当我有一个包含对临时表的“exec”调用的存储过程时,我会得到这个,例如:
insert into #codes (Code, ActionCodes, Description)
exec TreatmentCodes_sps 0
实体框架似乎对过程应返回的内容感到困惑。我遇到的解决方案是在 sproc 的顶部添加它:
SET FMTONLY OFF
在此之后,一切都很好。
只需更改为
ALTER PROCEDURE [dbo].[usp_GetTestRecords]
--@p1 int = 0,
--@p2 int = 0
@groupId nvarchar(10) = 0
AS
BEGIN
SET NOCOUNT ON;
SELECT * FROM CUSTOMERS WHERE Id = @groupId
END
我遇到了同样的问题,并在这里找到了解决方案
- 移至 .edmx
- 在“模型浏览器窗口/函数导入”中,找到您的过程,然后双击它
- 将返回类型更改为所需的返回类型
- 保存 .edmx 并再次检查返回类型。
它应该是你现在需要的。
评论
我知道这是一个古老的线程,但如果有人遇到同样的问题,我会告诉我的困境。
为了帮助查找问题,请在添加存储的过程时运行 sql profiler。然后,您可以看到哪个实体框架作为参数传递以生成结果集。我想它几乎总是会传递 null 参数值。如果您通过连接字符串值和参数值来动态生成 sql,并且有些值为 null,则 sql 将中断,您将不会获得返回集。
我不需要生成临时表或任何东西,只需要一个 exec 命令。
希望对您有所帮助
我遇到了同样的问题,我通过“AS”关键字更改了返回字段的名称并解决了我的问题。此问题的一个原因是使用 SQL Server 保留关键字命名列名。
这个例子是休耕:
ALTER PROCEDURE [dbo].[usp_GetProducts]
AS
BEGIN
SET NOCOUNT ON;
SELECT
, p.Id
, p.Title
, p.Description AS 'Description'
FROM dbo.Products AS p
END
如果存储过程在结果集中没有主键,则实体框架将自动返回标量值。因此,必须在 select 语句中包含主键列,或者创建具有主键的临时表,以便 Entity Framework 返回存储过程的结果集。
请参阅 Ladislav Mrnka 在此 Stack Overflow 帖子中的回答 https://stackoverflow.com/a/7131344/4318324
我有同样的基本问题。
添加
SET FMTONLY OFF
在导入过程中尝试导入的过程将解决此问题。 最好在之后删除该行,除非数据库的目的只是为 EF(实体框架)提供架构。
谨慎的主要原因是 EF 在尝试获取元数据时使用此设置来防止数据突变。
如果从数据库刷新实体模型,则任何包含此行的过程都可能通过尝试获取架构来更新该数据库中的数据。
我想对此添加进一步的注释,因此不需要完全扫描另一个链接。
如果您想尝试使用 FMTONLY,请记住以下几点。
when FMTONLY is on: 1) only the schema is returned (no) rows. similar to adding a blanket false statement to your where clause (ie "where 1=0") 2) flow control statements are ignored
例
set fmtonly on if 1=1 begin select 1 a end else begin select 1 a,2 b end while 1=1 select 1 c
上面返回 NO 行和三个查询中每个查询的元数据
出于这个原因,有些人建议以一种利用它不遵守流量控制的方式将其关闭
if 1=0 begin set fmtonly off end
事实上,你可以用它来引入跟踪这一点的逻辑
set fmtonly off declare @g varchar(30) set @g = 'fmtonly was set to off' if 1=0 begin set fmtonly off set @g = 'fmtonly was set to on' end select @g
在尝试使用此功能之前,请仔细考虑,因为它已被弃用,并且可能使 sql 极难遵循
需要理解的主要概念如下
1. EF turns FMTONLY on to prevent MUTATING data from executing stored procedures when it executes them during a model update. (from which it follows) 2. setting FMTONLY off in any procedure that EF will attempt to do a schema scan (potentially ANY and EACHONE) introduces the potential to mutate database data whenever *anyone* attempts to update their database model.
评论
我对此进行了一些思考,我认为我有一个更好/更简单的答案
如果你有一个复杂的存储,这给实体框架带来了一些困难(对于使用 FMTONLY 标记来获取架构的当前版本的实体框架)
请考虑在存储过程开始时执行以下操作。
--where [columnlist] matches the schema you want EF to pick up for your stored procedure if 1=0 begin select [columnlist] from [table list and joins] where 1=0 end
如果可以,可以将结果集加载到表变量中 您可以执行以下操作来帮助使架构保持同步
declare @tablevar as table ( blah int ,moreblah varchar(20) ) if 1=0 begin select * from @tablevar end ... -- load data into @tablevar select * from @tablevar
评论
如果需要执行此操作,则最好只创建 dbcontext 的一部分,然后自己创建 C# 函数,该函数将使用 SqlQuery 返回所需的数据。与其他一些选项相比,优点是:
- 模型更新时无需更改任何内容
- 如果您直接在生成的类中执行此操作,则不会被覆盖(上面有人提到这一点,就好像这是一个选项:))
- 不必向过程本身添加任何现在或以后可能产生副作用的内容
示例代码:
public partial class myEntities
{
public List<MyClass> usp_GetTestRecords(int _p1, int _p2, string _groupId)
{
// fill out params
SqlParameter p1 = new SqlParameter("@p1", _p1);
...
obj[] parameters = new object[] { p1, p2, groupId };
// call the proc
return this.Database.SqlQuery<MyClass>(@"EXECUTE usp_GetTestRecords @p1, @p2, @groupId", parameters).ToList();
}
}
导入期间
将 FMTONLY 设置为 ON 可用于采用 SP 架构。
如果更改 sp 并想要更新新函数,则应从 edmx 文件(从 xml)中删除旧的定义函数,因为尽管从模型浏览器中删除了 sp,但在 edmx 中不会删除它。例如;
<FunctionImport Name="GetInvoiceByNumber" ReturnType="Collection(Model.Invoice_Result)">
<Parameter Name="InvoiceNumber" Mode="In" Type="Int32" />
</FunctionImport>
我遇到了同样的问题,当我完全删除相应 sp 的 FuctionImport 标签时,模型更新正确。可以通过在 Visual Studio 中搜索函数名称来查找标记。
您可能很幸运地打开模型浏览器,然后转到“函数导入”,双击有问题的存储过程,然后手动单击“获取列信息”,然后单击“创建新的复杂类型”。这通常可以解决问题。
好吧,我也遇到了这个问题,但是经过数小时的在线搜索,上述方法都无济于事。 最后,我知道如果您的存储过程将某些参数获取为 null 并在查询执行中生成任何错误,就会发生这种情况。 实体框架将通过定义复杂的实体模型来生成存储过程的方法。由于该 null 值,存储过程将返回 int 值。
请检查您的存储过程,它是否提供带有 null 值的空结果集。它将解决您的问题。希望。
我发现的最好的解决方案是作弊一点。
在存储过程中,注释所有内容,将第一行与等...
现在更新模型,转到映射函数,选择复杂类型,然后单击 ,然后单击 。select [foo]='', [bar]=''
Get Column Information
Create Complex Type
现在对假选择进行注释,并取消对真实存储过程正文进行注释。
我认为这是数据库权限的问题,我不知道到底是什么,但是,在我的工作中,我们使用 Active Directory 用户授予应用程序连接到数据库的权限,这些帐户是专门为应用程序创建的,每个应用程序都有自己的用户帐户,好吧,作为开发人员,我有读取权限, 编写和其他基本的东西,没有更改,也没有高级功能,我用我的普通帐户运行 Visual Studio 时遇到了同样的问题,然后,我所做的是打开 Visual Studio,在上下文菜单上选择“作为其他用户”选项,然后我把授予应用程序的 AD 登录名,瞧! 现在我的存储过程正在加载我预期的所有字段,在此之前,我的存储过程以 int 形式返回。我希望这对某人有所帮助,也许数据库帐户的VIEW DEFINITION权限可以解决问题
如果 SQL 身份验证已到位,请验证用于将 Entity Framework 连接到数据库的用户凭据是否具有从 CUSTOMERS 表中读取的适当权限。
当实体框架使用 SQL 身份验证映射复杂对象(即 SELECT 多列的存储过程)时,如果此类存储过程中的任何表未设置“读取”权限,则映射将导致返回 INT 而不是所需的结果集。
上一个:迭代文件夹和子文件夹的最佳方式
评论
usp_GetTestRecords()