繁体   English   中英

存储过程返回 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);
}

当我有一个包含对临时表的“exec”调用的存储过程时,我会得到这个,例如:

insert into #codes (Code, ActionCodes, Description)
exec TreatmentCodes_sps 0

实体框架似乎对程序应该返回什么感到困惑。 我遇到的解决方案是在 sproc 的顶部添加它:

SET FMTONLY OFF

在此之后,一切都很好。

我遇到了同样的问题,并在这里找到了解决方案

  1. 移至您的 .edmx
  2. 在模型浏览器窗口/函数导入中找到您的程序,然后双击它
  3. 将返回类型更改为您想要的
  4. 保存 .edmx 并再次检查返回类型。

截屏

它应该是你现在需要的。

实体框架无法判断您的存储过程返回什么。 我已经成功创建了一个表变量,该变量反映了 SELECT 语句中的数据。 只需插入表变量,然后从该表变量中进行选择。 EF 应该把它捡起来。

参见 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

以上不返回任何行和三个查询中的每一个的元数据

出于这个原因,有些人建议以一种利用它不遵守流量控制的方式关闭它

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.

如果您的存储过程在结果集中没有主键,实体框架将自动返回一个标量值。 因此,您必须在 select 语句中包含一个主键列,或者创建一个带有主键的临时表,以便实体框架为您的存储过程返回结果集。

当您为存储过程生成模型类时,您错误地选择了标量返回结果。 您应该从实体模型中删除存储过程,然后重新添加存储过程。 在存储过程的对话框中,您可以选择期望的返回类型。 不要只编辑生成的代码.. 这现在可能有效,但如果您对模型进行其他更改,则可以替换生成的代码。

我遇到了同样的问题,我通过“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

我考虑了一下,我想我有一个更好/更简单的答案

如果您有一个复杂的存储,这给实体框架带来了一些困难(对于使用 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 返回您需要的数据。 与其他一些选项相比的优点是:

  1. 模型更新时不必更改任何内容
  2. 如果您直接在生成的类中执行它,则不会被覆盖(上面有人提到这一点,好像它是一个选项:))
  3. 不必向 proc 本身添加任何现在或以后可能产生副作用的东西

示例代码:

 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();
       }
    }

我发现的最佳解决方案是作弊。

在存储过程中,注释所有内容,在第一行放一个select [foo]='', [bar]=''等... 现在更新模型,转到映射函数,选择复杂类型并单击Get Column Information ,然后Create Complex Type

现在评论假的选择并取消评论真实的存储过程主体。

只需更改为

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

我知道这是一个旧线程,但如果有人遇到同样的问题,我会说出我的困境。

作为查找问题的帮助,请在添加存储过程时运行 sql profiler。 然后你可以看到什么实体框架作为参数传递来生成你的结果集。 我想它几乎总是会传递空参数值。 如果您通过连接字符串值和参数值动态生成 sql 并且一些为空,那么 sql 将中断并且您不会获得返回集。

我不需要生成临时表或任何只是一个 exec 命令。

希望能帮助到你

导入过程中

SET 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 并且在查询执行中产生任何错误,就会发生这种情况。 实体框架将通过定义复杂的实体模型来生成存储过程的方法。 由于该空值,您的存储过程将返回 int 值。

请检查您的存储过程,或者它是否提供带有空值的空结果集。 它会解决你的问题。 希望。

我认为这是数据库权限问题,我不知道到底是什么问题,但是,在我的工作中,我们使用 Active Directory 用户授予应用程序连接到数据库的权限,这个帐户是专门为应用程序创建的,每个应用程序有自己的用户帐户,好吧,作为开发人员,我拥有读取、写入和其他基本内容的权限,没有更改,也没有高级功能,而且我在使用普通帐户运行 Visual Studio 时遇到了同样的问题,然后,我做了什么是打开 Visual Studio,在上下文菜单上选择“作为不同的用户”选项,然后我为应用程序授予 AD 登录权限,瞧!现在我的存储过程正在加载我预期的所有字段,在此之前,我的存储过程以 int 形式返回。 我希望这对某人有所帮助,也许数据库帐户的 VIEW DEFINITION 权限可以解决问题

如果 SQL 身份验证到位,请验证用于将 Entity Framework 连接到数据库的用户凭据是否具有从 CUSTOMERS 表读取的正确权限。

当实体框架使用 SQL 身份验证映射复杂对象(即选择多列的存储过程)时,如果此类存储过程中的任何表没有设置读取权限,则映射将导致返回 INT 取而代之所需的结果集。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM