2016-12-11 101 views
2

我有一個使用實體框架(代碼優先)管理的SQL Server後端的WebAPI服務。我已經使用Linq查詢從數據庫中檢索數據。由於我的一些API端點執行多個連接來獲取數據,因此在部署到生產環境中時我觀察到了一些延遲。我閱讀了使用ORM映射與直接SQL查詢(存儲過程),並得出結論,對於複雜查詢,使用存儲過程是最佳選擇。從Entity Framework中的存儲過程返回最終結果

我碰到一些文章來解釋如何從存儲過程中使用類似的代碼返回值:

context.Database.SqlQuery<T>(...) 

但是,與簡單SELECT查詢例子交易,是的,我還隔着返回多個SELECT查詢結果出來太。但在這種情況下,這是2 SELECT查詢。

在實際情況中,由於可能涉及連接和其他代碼,因此可能會有2個以上的選擇查詢。而且,在查詢結束時,我們只會推斷最後的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 [email protected] And [email protected] 

我怎樣才能從去年SELECT查詢行實現這一目標?

我做了一些研究,並且認爲使用EF是不可能的,因爲存儲過程支持是最小的。有可能在EF範圍內做,或者我需要回到ADO.NET嗎?

任何幫助將不勝感激。

謝謝。

+0

@GertArnold - 我已經添加示例代碼段解釋的情況。 – abhilashca

+0

我想你應該用'選擇'你的實際列列表'來替換'選擇*',這樣你才能真正知道你在做什麼。然後創建一個類型爲'MyResultType'的whos屬性來匹配選定的列並調用'context.Database.SqlQuery (...)'。 – grek40

+0

是的,我已經嘗試過,但它是鑄造到'Table1DTO'而不是'Table3DTO'當然會拋出鑄造錯誤。 – abhilashca

回答

1

我在閱讀MSDN文章 - https://msdn.microsoft.com/en-us/data/jj691402.aspx後,設法推斷出了一個解決方案。這是我所做的。

我執行存儲在使用DbCommand.ExecuteReader()其返回DbDataReader過程。因爲,我的興趣點是最後的SELECT聲明。我設法跳到DbCommandReader的最後一個結果集,並使用ObjectContext.Translate<T>()將其轉換爲我的DTO

不知道是否有任何更好的可選可用。暫時有效。

更新:我終於切換到Dapper這是一個來自StackExchange的微型ORM。與EF相比,它顯着提升了性能。 EF調用了3秒的API調用現在可以在不到500毫秒的時間內返回數​​據。

謝謝。

+0

我在寫我的答案時也在看那篇文章。使用對象上下文肯定幫助我進行調試,因爲它的方法拋出了一些更有用的異常。但是一旦問題得到解決,我認爲''SqlQuery'是比較清晰的解決方案,而不是回到對象上下文。 – grek40

+0

你有什麼建議,哪種性能方式可以實現?在高層次上,因爲我正在處理'ExecuteReader()',所以我可以精確地選擇'SELECT'語句,我需要將其轉換爲'T'類型。如果我選擇'SqlQuery',那麼系統需要比較返回類型'T'和每個'Select'語句,以查看在投射之前哪些是匹配的。這是否有任何性能問題?有什麼想法嗎? – abhilashca

+0

實際上,由於您只對最後一個查詢感興趣,因此您可能需要重新設計查詢,使其成爲唯一返回結果的查詢。也許看看[With - As - Select]](https://msdn.microsoft.com/zh-cn/library/ms175972.aspx)子句來替換您的過程的內部預選。要編輯一個SP例子到我的問題。 – grek40

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" }, 
     } 
    }, 
}; 

和存儲過程由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 
檢索 Posts

然後你可以創建一個arb與性質,其名稱和類型itrary類匹配您的過程結果

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

+0

此代碼是否可用?我實際上在最後一天嘗試了同樣的事情,但它沒有爲我工作。讓我試試你的樣本。謝謝。 – abhilashca

+0

我必須要格外注意在SP默認值賦給'BlogId',包括命令字符串中的SP參數(不靠'SqlParameter'名稱相同),但是,是的,我真的執行這個它爲我工作,沒有假設的例子。 – grek40

+0

我試過這個例子。這是我得到的。 'ErrorMessage =發生錯誤。,數據讀取器與指定的'MY_TYPE_NAME_HERE'不兼容。的這種類型的構件,「PROPERTY_NAME_HERE」,不具有所述數據讀取器的相應的柱,用相同的名稱。在System.Data.Entity.Core.Query.InternalTrees.ColumnMapFactory.GetMemberOrdinalFromReader(DbDataReader storeDataReader,EdmMember構件,EdmType currentType,Dictionary'2 renameList)'。 – abhilashca

0

不使用時只需執行。

使用此SQL:

Insert into #tempTable 
execute sp_executesql @SELECT 
相關問題