2016-12-11 4 views
2

Entity Framework(コードファースト)を使用して管理されるSQL Serverバックエンドを持つWebAPIサービスがあります。 Linqクエリを使用してデータベースからデータを取得しました。私のAPIエンドポイントのいくつかはデータを取得するために複数の結合を行っているため、本番環境に配備するといくらか遅延が見られました。 ORMマッピングと直接SQL問合せ(ストアド・プロシージャ)を使用して読んだところ、複雑な問合せではストアド・プロシージャを使用するのが最適であるという結論に達しました。Entity Frameworkのストアドプロシージャの最終結果を返します

私のようなコードを使用してストアドプロシージャから値を返す方法を説明するいくつかの記事に出くわした:しかし、すべての例は、単純なSELECTクエリを扱うとはい、私はまた、複数のSELECTクエリ結果を返すに出くわした

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

あまりにも。しかし、その場合は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

'Select *'を 'Select your column columns list 'に置き換えて、あなたが実際に作業していることを実際に知っているはずです。次に、選択された列に一致するプロパティ 'MyResultType'を作成し、' context.Database.SqlQuery (...) 'を呼び出します。 – grek40

+0

はい、私はすでに試してみましたが、キャストエラーを投げる 'Table3DTO'ではなく、' Table1DTO'にキャストしています。 – abhilashca

答えて

1

I何とかMSDN記事 - https://msdn.microsoft.com/en-us/data/jj691402.aspxを読んだ後に解決策を推論することができました。ここに私がしたことがあります。

DbCommand.ExecuteReader()を使用してストアドプロシージャを実行しました。これはDbDataReaderです。以来、私の興味のポイントは、最後のSELECTステートメントです。私はDbCommandReaderの最後の結果セットにスキップし、DTOObjectContext.Translate<T>()を使用してキャストしました。

より良いオプションがあるかどうかはわかりません。当分の間、それは動作します。

更新:最終的にStackExchangeのマイクロORMであるDapperに切り替えました。これはEFと比較して大幅なパフォーマンス向上をもたらします。 EFで3秒かかるAPI呼び出しは、500ミリ秒未満でデータを返すようになりました。

ありがとうございました。

+0

私は自分の答えを書いたときにその記事を見ていました。オブジェクトのコンテキストを使用することは、そのメソッドがいくつかのより有益な例外を投げかけていたため、デバッグ時に私を助けてくれました。しかし、いったん問題が整理されると、私は 'SqlQuery'はオブジェクト・コンテキストに戻ることに比べてより洗練された解決策であると考えています。 – grek40

+0

パフォーマンスの実装方法は何ですか?高レベルでは、 'ExecuteReader() 'を扱っているので、私は' T'型にキャストするために必要な 'SELECT'文を正確に選ぶことができました。私が 'SqlQuery'を選択すると、戻り型' T'とそれぞれの 'Select'文を比較して、どの型がキャスト前のものであるかを調べる必要があります。これにパフォーマンス上の問題はありますか?何かご意見は? – abhilashca

+0

実際には、最後のクエリだけに興味があるので、結果を返す唯一のものになるようにクエリを再設計することができます。おそらく、プロシージャの内部の事前選択を置き換えるために[With-As-Select'](https://msdn.microsoft.com/en-us/library/ms175972.aspx)節を見てください。私の質問にSPの例を編集するつもりです。 – grek40

0

コードの大部分を公開していないので、私はあなたのサンプルを提供することができます。 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を取得する

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

ストアドプロシージャ:

あなたは、私がデータベース内に存在すると仮定し、次の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; } 
} 

いくつかのデータを持っていると仮定します

次に、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パラメータを含めるために、SPの 'BlogId'にデフォルト値を割り当てるように注意する必要がありました(' 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 
関連する問題