使用Entity Framework 6无法获取存储过程结果

6

我有一个存储过程,它会返回一个 01,取决于指定的电子邮件地址是否存在于我的数据库中:

CREATE PROCEDURE [DatabaseSchema].[EmailAddressIsDuplicate] (@emailAddress nvarchar(255))
AS
BEGIN
    SET NOCOUNT ON;

    IF EXISTS(
        SELECT *
        FROM [DatabaseSchema].[EmailUpdatesRegistrant]
        WHERE EmailAddress = @emailAddress
    )
        RETURN 1
    ELSE
        RETURN 0

    RETURN 0
END

GO

我试图从 Entity Framework 6 数据库上下文中推导出这个存储过程的结果:

using (DatabaseContext dbContext = new DatabaseContext())
{
    ObjectParameter param = new ObjectParameter("emailAddress", typeof(bool));
    var result = dbContext.EmailAddressIsDuplicate(emailAddress); 
}

我收到了许多错误信息。

Error #1: 使用以上代码,var result 始终设置为 -1。

Error #2: 我尝试导航到 Edit Function Import 并将 Returns a Collection Of 设置为一个 Boolean 标量值。这会抛出以下错误:

由存储数据提供程序返回的数据读取器没有足够的列用于请求的查询。

Error #3: 我回去并将 Edit Function Import 的返回值设为 None。然后我尝试了这个答案中的以下代码:

using (DatabaseContext dbContext = new DatabaseContext())
{
    var p = new SqlParameter("@emailAddress", emailAddress);
    var result = dbContext.Database.SqlQuery<bool>("DatabaseSchema.EmailAddressIsDuplicate", p);
}

没有立即抛出错误,但我不知道是否可以从var result中推导出有用的数据。尝试将result转换为bool会抛出以下错误:

无法将类型'System.Data.Entity.Infrastructure.DbRawSqlQuery'转换为'bool'

你有什么办法可以查看存储过程的结果(01)吗?

目前EmailAddressIsDuplicate(emailAddress)的返回类型是什么? - DaniDev
4个回答

2
原因 - EF(包括v6)的模板构建器错误地将SP设置为返回一个包含行数的INT,而不是返回值,因为它错误地调用了错误的ExecuteFunction(在模板生成的类YourDatabaseEntities中找到,它是DBContext的子类)。
为什么会出现错误的ExecuteFunction?- 结果集错误地显示了更改行数而不是返回值或输出参数,因为它调用了不同的ExecuteFunction,该函数会丢弃结果。 ObjectContext.ExecuteFunction的悬停智能提示说“执行存储过程…;丢弃从函数返回的任何结果;并返回执行影响的行数”,而不是通常的“执行存储过程…。使用指定的参数”。
为什么是-1:我认为SET NOCOUNT ON导致SP返回没有计数结果,Microsoft的ExecuteFunction将其作为错误代码返回。 SP修复 - 1)您必须注释掉SET NOCOUNT ON。2)您必须更改存储过程,使SELECT命令成为最后一条语句,而不是RETURN命令。 解决方案修复 - 1)修复SP后,从Function Imports文件夹和Data Store的SP文件夹中删除SP。2)通过使用“从数据库更新模型”重新加载SP到EDMX中。3)重建EDMX所在的所有数据项目。4)退出Visual Studio并返回。5)重新构建整个解决方案。
参见:Entity Framework(Database first)存储过程的返回结果不正确

非常棒的回答!EF6的bug也是我的结论,但看到如此详尽的解释真是有趣。 - Zeek2

2
您可以尝试在存储过程签名中添加一个输出参数(@result):
CREATE PROCEDURE [DatabaseSchema].[EmailAddressIsDuplicate]
    (@emailAddress nvarchar(255), @result bit out)
AS
BEGIN
    SET NOCOUNT ON;

    IF EXISTS(SELECT *
              FROM [DatabaseSchema].[EmailUpdatesRegistrant]
              WHERE EmailAddress = @emailAddress)
       SET @result = 1
    ELSE
       SET @result = 0

    RETURN @result
END
GO

(您需要相应重新定义EF模型函数的定义)
using (DatabaseContext dbContext = new DatabaseContext())
{
    ObjectParameter isDuplicate = new ObjectParameter("isDuplicate", typeof(bool)); 
    var result = dbContext.EmailAddressIsDuplicate(emailAddress, isDuplicate);

    bool emailIsDuplicate = (bool)isDuplicate.Value;.    
}

如果您想直接调用带有输出参数的存储过程,可以遵循以下建议:Database.SqlQuery calling stored procedure that has multiple output parameters


如果您无论如何都要返回一个整数,那么在不使用中间输出参数的情况下,至少可以从存储过程中返回行计数。 - vamsi
我最初用返回 Bit 类型编写了我的建议。 @alex 可以尝试两种方式。 - DaniDev
1
这最终帮助我找到了答案。我更新了存储过程,并使用以下代码:ObjectParameter isDuplicate = new ObjectParameter("isDuplicate", typeof(bool)); var result = dbContext.EmailAddressIsDuplicate(emailAddress, isDuplicate); bool emailIsDuplicate = (bool)isDuplicate.Value;。使用 int retParam 将会抛出编译时错误。请使用这个可行的代码更新你的答案,我会标记它为已接受 - 谢谢! - alex
好的,我原本打算包括参数定义。你实现得很好,谢谢,祝好。 - DaniDev

0

使用参数在C#中实现存储过程的值。

资源:https://msdn.microsoft.com/zh-cn/library/yy6y35y8(v=vs.110).aspx

这样,可以从ExecuteReader将值存储到变量中。

将该值添加到模型中,类似于将值添加到属性中。存储过程可以从ActionResult中调用。虽然这可能需要将存储过程添加到单独的层中,但该层仅运行存储过程,并在此之后将值添加到模型中。


我可以像链接所示那样使用“SqlConnection”调用存储过程,但我正在尝试使用Entity Framework 6完成这个操作。 - alex
SqlConnection是否在与实体框架模型分离的单独层中?从SqlConnection和Reader检索值后,将其添加到所属值的模型属性中。 - JDavila
你不需要使用Entity Framework来打开SqlConnection。当然,我可以在没有Entity Framework的情况下做到这一点 - 但根本问题是Entity Framework与存储过程不兼容。这个回答了我的问题。 - alex

-1
尝试这个。
CREATE PROCEDURE [DatabaseSchema].[EmailAddressIsDuplicate] (@emailAddress nvarchar(255))
AS
BEGIN

        SELECT *
        FROM [DatabaseSchema].[EmailUpdatesRegistrant]
        WHERE EmailAddress = @emailAddress

    SELECT @@ROWCOUNT

END

GO


using (DatabaseContext dbContext = new DatabaseContext())
{ 
    var result = dbContext.Database.SqlQuery<int32>("exec  DatabaseSchema.EmailAddressIsDuplicate {0}", emailAddress).FirstOrDefault();
}

返回值中除了0以外的任何内容都表示有匹配项,数字表示匹配项的数量


那么 var result 的类型是 System.Data.Entity.Infrastructure.DbRawSqlQuery<int>,而 result[0] 抛出错误 Cannot apply indexing with [] to an expression of type 'System.Data.Entity.Infrastructure.DbRawSqlQuery<int>'。我该怎么处理 var result - alex
结果是一个数字,表示在数据库中找到的匹配项数量。 - vamsi
不是的,var result 是一个 System.Data.Entity.Infrastructure.DbRawSqlQuery<int> 类型,而不是整数。 - alex
对于你的目的来说,它仍然可以被视为C#整数。 - vamsi
我没有给你的答案点踩,但是这个答案是不正确的。看一下这里的文档 - 它说:返回值类型: System.Data.Entity.Infrastructure.DbRawSqlQuery<TElement>。这意味着 var result 不是一个整数。你需要枚举 SqlQuery 的结果来得到 int32 - alex
抱歉,我的错误。应该使用FirstOrDefault。我刚刚意识到它丢失了。我已经更改了答案以反映相同的内容。现在应该可以工作了。 - vamsi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接