存储过程返回int而不是结果集

时间:2013-08-15 02:47:37

标签: c# sql entity-framework

我有一个包含动态选择的存储过程。像这样:

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

17 个答案:

答案 0 :(得分:21)

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

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

似乎实体框架对程序应返回的内容感到困惑。我遇到的解决方案是将其添加到sproc的顶部:

SET FMTONLY OFF

在此之后,一切都很好。

答案 1 :(得分:19)

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

  1. 转到您的.edmx
  2. 在模型浏览器窗口/功能导入中找到您的过程,然后双击它
  3. 将退货类型更改为您想要的
  4. 保存.edmx并再次检查返回类型。
  5. Screenshot

    这应该是你现在需要的。

答案 2 :(得分:13)

实体框架无法告诉您的存储过程返回的内容。我成功创建了一个表变量,它反映了SELECT语句中的数据。只需插入表变量,然后从该表变量中选择。 EF应该拿起它。

答案 3 :(得分:6)

请参阅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.

答案 4 :(得分:4)

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

答案 5 :(得分:1)

我遇到了同样的问题,我通过&#39; AS&#39;更改了返回字段的名称。关键字并解决了我的问题。此问题的一个原因是使用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

答案 6 :(得分:1)

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

答案 7 :(得分:1)

我已经考虑过这一点,我想我有一个更好/更简单的答案

如果你有一个复杂的存储,它给某些实体框架带来了困难(对于使用FMTONLY标签来获取模式的当前版本的Entity Framework)

考虑在存储过程开始时进行以下操作。

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

答案 8 :(得分:1)

我发现最好的解决方法是作弊。

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

现在注释伪造的选择并取消注释真实的存储过程主体。

答案 9 :(得分:0)

只需更改为

SELECT 
   Convert(char(8), OrderH_dtmInitiated, 112) As BookingDate,   
   Datename (weekday, OrderH_dtmInitiated) As NameOfDay,
   [OrderH_strEmailConfirmationSent] 
  ,[OrderH_strEmail]  
FROM [VISTAIT].[dbo].[tblOrderHistory] 
WHERE  OrderH_strEmailConfirmationSent is NULL And OrderH_dtmInitiated >= (SELECT  DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()-1)))  
ORDER by Convert(char(8), OrderH_dtmInitiated, 112) 

答案 10 :(得分:0)

我知道这是一个旧帖子,但如果有人遇到同样的问题,我会告诉我的困境。

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

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

希望有所帮助

答案 11 :(得分:0)

如果你需要这样做,那么你可能最好只做一部分dbcontext并自己创建C#函数,它将使用SqlQuery返回你需要的数据。与其他一些选项相比的优势是:

  1. 在模型更新
  2. 时不必更改任何内容
  3. 如果您直接在生成的课程中进行覆盖,则不会被覆盖(有人提到这个,就好像它是一个选项:))
  4. 不必向proc本身添加任何可能现在或稍后产生副作用的内容
  5. 示例代码:

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

答案 12 :(得分:0)

导入期间

设置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搜索功能名称来找到标签。

答案 13 :(得分:0)

打开模型浏览器可能很幸运,然后转到“函数导入”,双击有问题的存储过程,然后手动单击“获取列信息”,然后单击“创建新的复杂类型”。通常可以解决问题。

答案 14 :(得分:0)

我也遇到了这个问题,但是经过数小时的在线搜索,上述方法都无济于事。 最终我知道,如果您的存储过程将某些参数设为null并在查询执行中产生任何错误,则将发生这种情况。 实体框架将通过定义复杂实体模型来生成存储过程的方法。由于该空值,您的存储过程将返回int值。

请检查您的存储过程,或者其提供的空结果集为空值。它将解决您的问题。希望如此。

答案 15 :(得分:0)

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

答案 16 :(得分:0)

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

当Entity Framework使用SQL身份验证来映射复杂对象(即,选择一个以上列的存储过程)时,如果该存储过程中的任何表都未设置读取权限,则该映射将导致返回INT而不是所需的结果集。