从Entity Framework中的存储过程返回最终结果

时间:2016-12-11 17:57:22

标签: c# entity-framework linq

我有一个带有SQL Server后端的WebAPI服务,它使用Entity Framework(代码优先)进行管理。我使用Linq查询从数据库中检索数据。由于我的一些API端点执行多个连接以获取数据,因此在生产中部署时发现了一些延迟。我读到使用ORM映射与直接SQL查询(存储过程),并得出结论,对于复杂查询,使用存储过程是最佳的。

我发现了一些文章,解释了如何使用以下代码从存储过程返回值:

// I have provided a function
<button onClick={ function(event){ 
  this.onDeleteClick(event.target.value) }
} }>
  <span>Remove</span>
</button> 

但是,所有示例都涉及简单的context.Database.SqlQuery<T>(...) 查询,是的,我也遇到了多个SELECT查询结果。但在这种情况下,它是2 SELECT个查询。

在实际情况中,它可能超过2个选择查询,因为它可能涉及连接和其他代码。并且,在查询结束时,我们只会推断出返回预期数据的最终SELECT查询。下面是一个示例代码来解释场景:

SELECT

如何从最后// Inside my stored procedure Declare @XId uniqueidentifier Declare @YId uniqueidentifier // Some mockup queries. This can have JOINS and other complex codes in real code. // Below code is made as simple as possible to explain the scenario Select @XId=Id From Table1 Where Name = 'Name1' Select @YId=Id From Table2 Where Code = 'Code1' // This is the data, I am interested Select * From Table3 Where Col1Id=@XId And Col2Id=@YId 个查询中获取行?

我做了一些研究,并认为由于存储过程支持很少,因此不可能使用EF。这可能在EF限制内完成,还是我需要回到ADO.NET呢?

任何帮助将不胜感激。

感谢。

3 个答案:

答案 0 :(得分:1)

在阅读MSDN文章后,我以某种方式设法推断出了一个解决方案 - https://msdn.microsoft.com/en-us/data/jj691402.aspx。这就是我所做的。

我使用返回DbCommand.ExecuteReader()的{​​{1}}执行存储过程。因为,我的兴趣点是最后一个DbDataReader声明。我设法跳到SELECT的最后一个结果集,然后使用DbCommandReader将其转换为DTO

不确定是否有更好的可选项。暂时它有效。

更新:我终于切换到Dapper这是StackExchange的微型ORM。与EF相比,它可以显着提升性能。使用EF花费3秒的API调用现在可以在不到500毫秒的时间内返回数​​据。

感谢。

答案 1 :(得分:0)

由于您没有透露很多代码,我只能为您提供工作样本。

假设您拥有以下CodeFirst架构

class DbC : DbContext
{
    public DbC()
    {
    }
    public DbSet<Blog> Blogs { get; set; }
}

class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Post> Posts { get; set; }
}

class Post
{
    public int Id { get; set; }
    public string Text { get; set; }
}

我假设驻留在数据库中的一些数据:

var tmp = new List<Blog>()
{
    new Blog
    {
        Name = "Blog1",
        Posts = new List<Post>()
        {
            new Post { Text = "Post1" },
            new Post { Text = "Post2" },
            new Post { Text = "Post3" },
        }
    },
    new Blog
    {
        Name = "Blog2",
        Posts = new List<Post>()
        {
            new Post { Text = "Post4" },
            new Post { Text = "Post5" },
            new Post { Text = "Post6" },
        }
    },
};

通过Posts

检索Blog.Name的存储过程
CREATE PROCEDURE [dbo].[PostsRetrieverProc]
    @BlogName NVARCHAR (MAX)
AS
    Declare @BlogId int = 0

    Select @BlogId=Id From [Blogs] Where Name = @BlogName

    Select
        b.Id [BlogId],
        p.Id [PostId],
        p.[Text] [PostText]
    From [Blogs] b join [Posts] p On b.Id = p.Blog_Id
    Where p.Blog_Id =  @BlogId
GO

然后,您可以使用名称和类型与您的过程结果匹配的属性创建一个任意类

public class MyCustomReturnType
{
    public int BlogId { get; set; }
    public int PostId { get; set; }
    public string PostText { get; set; }
}

并调用存储过程返回一些值

using (var db = new DbC())
{
    var result = db.Database.SqlQuery<MyCustomReturnType>("dbo.PostsRetrieverProc @param1", new SqlParameter("param1", "Blog2"));
    foreach (var item in result)
    {
        Console.WriteLine(item.PostText);
    }
}

我的结果按预期输出:

Post4
Post5
Post6

这种方法有什么问题吗?

修改

由于您只需要存储过程的最后一个结果,因此最好重新设计SP而不是跳过中间结果。这可以使用With clause来完成。请注意,此示例代码有点臃肿,因为我只使用博客ID并仍然与[Blogs]一起加入以显示其可能的

CREATE PROCEDURE [dbo].[PostsRetrieverProc]
    @BlogName NVARCHAR (MAX)
AS
    With
        BlogEntries (BlogId)
    As
        (Select Id BlogId From [Blogs] Where Name = @BlogName)
    Select
        b.Id [BlogId],
        p.Id [PostId],
        p.[Text] [PostText]
    From BlogEntries e
    join [Blogs] b On e.BlogId = b.Id
    join [Posts] p On b.Id = p.Blog_Id

允许多次预先选择也很有意思:Can I use multiple "with"?

答案 2 :(得分:0)

不要只使用执行。

使用这个sql:

Insert into #tempTable 
 execute  sp_executesql @SELECT