37

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

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

17 回答 17

31

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

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

实体框架似乎对过程应该返回什么感到困惑。我遇到的解决方案是将其添加到存储过程的顶部:

SET FMTONLY OFF

在此之后,一切都很好。

于 2014-10-21T13:39:24.480 回答
30

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

  1. 移至您的 .edmx
  2. 在 Model Browser Window/Function Imports 找到您的程序,然后双击它
  3. 将返回类型更改为您想要的
  4. 保存 .edmx 并再次检查返回类型。

截屏

它应该是你现在需要的。

于 2015-12-08T08:33:04.243 回答
15

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

于 2013-08-15T15:45:13.417 回答
10

请参阅 Ladislav Mrnka 在此 Stack Overflow 帖子中的回答 https://stackoverflow.com/a/7131344/4318324

我有同样的基本问题。

添加

SET FMTONLY OFF

在导入期间尝试导入的程序将解决此问题。除非数据库的目的仅仅是为 EF(实体框架)提供架构,否则最好在之后删除该行。

谨慎的主要原因是 EF 在尝试获取元数据时使用此设置来防止数据突变。

如果您从数据库刷新实体模型,则其中包含此行的任何过程都可能仅通过尝试获取架构来更新该数据库中的数据。

我想对此添加进一步说明,因此不需要完全扫描其他链接。

如果您想尝试使用 FMTONLY,请记住以下几点。

当 FMTONLY 开启时:
  1) 只返回模式 (no) 行。
     类似于在您的 where 子句中添加一揽子错误语句(即“where 1=0”)
  2) 流控制语句被忽略

例子

设置 fmtonly

如果 1=1
开始
    选择 1 个
结尾
别的
开始
    选择 1 a,2 b
结尾

而 1=1
选择 1 c

上面没有返回任何行以及三个查询中的每一个的元数据

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

如果 1=0
开始
    关闭 fmtonly
结尾

实际上,您可以使用它来引入跟踪它的逻辑

关闭 fmtonly
声明@g varchar(30)
set @g = 'fmtonly 设置为关闭'

如果 1=0
开始
    关闭 fmtonly
    set @g = 'fmtonly 设置为开启'
结尾

选择@g

在尝试使用此功能之前请仔细考虑,因为它已被弃用并且可能使 sql 极难遵循

需要理解的主要概念如下

1、EF开启FMTONLY,防止MUTATING数据执行存储过程
   当它在模型更新期间执行它们时。
   (由此而来)

2. 在 EF 将尝试进行模式扫描的任何过程中将 FMTONLY 设置为关闭
   (可能是 ANY 和 EACHONE)引入了变异数据库的可能性
   每当*任何人*尝试更新他们的数据库模型时的数据。
于 2017-04-06T20:32:06.710 回答
7

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

于 2016-10-10T18:33:04.017 回答
2

我遇到了同样的问题,我通过“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
于 2016-06-21T04:50:16.657 回答
2

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

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

现在评论假选择并取消评论真正的商店过程主体。

于 2018-11-28T02:04:03.107 回答
1

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

于 2013-08-15T12:03:24.337 回答
1

我已经考虑了一下,我认为我有一个更好/更简单的答案

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

考虑在存储过程的开头执行以下操作。

--where [columnlist] 匹配您希望 EF 为您的存储过程选择的架构

如果 1=0
开始
    选择
       [专栏]
    来自 [表列表和连接]
    其中 1=0
结尾

如果您可以将结果集加载到表变量中,您可以执行以下操作以帮助保持架构同步

将@tablevar 声明为表
(
    废话
    ,moreblah varchar(20)
)

如果 1=0
开始
    从 @tablevar 中选择 *
结尾

...
-- 将数据加载到@tablevar
从 @tablevar 中选择 *
于 2017-08-14T19:42:57.073 回答
1

如果您需要这样做,那么您最好只制作部分 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();
       }
    }
于 2018-01-08T19:38:16.327 回答
0

只需更改为

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
于 2015-05-16T08:56:40.920 回答
0

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

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

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

希望能帮助到你

于 2015-12-09T15:28:20.843 回答
0

导入期间

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 中搜索函数名称来找到标记。

于 2018-01-22T07:23:02.977 回答
0

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

于 2018-08-03T08:33:15.383 回答
0

好吧,我也遇到了这个问题,但是经过数小时的在线搜索,上述方法都没有帮助。最后我知道,如果您的存储过程将某些参数设置为 null 并在查询执行中产生任何错误,就会发生这种情况。Entity Framework 将通过定义复杂的实体模型来生成存储过程的方法。由于该 null 值,您的存储过程将返回 int 值。

请检查您的存储过程是否提供具有空值的空结果集。它将解决您的问题。希望。

于 2018-08-28T19:53:06.800 回答
0

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

于 2019-10-08T00:59:55.153 回答
0

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

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

于 2020-05-13T16:23:13.950 回答