web-dev-qa-db-ja.com

EntityFrameworkを介してテーブル値タイプをSQLServerストアドプロシージャに渡す

SQLServerでユーザー定義のテーブルタイプを作成しました。

CREATE TYPE dbo.TestType AS TABLE 
(
    ColumnA int,
    ColumnB nvarchar(500)
)

そして、私はストアドプロシージャを使用して、データベースにレコードを挿入しています。

create procedure [dbo].[sp_Test_CustomType]
   @testing TestType READONLY
as
    insert into [dbo].[myTable]
        select ColumnA, ColumnB 
        from @testing

そして、EFを使用してこのストアドプロシージャを実行したいのですが、ここに問題があります。ユーザー定義のテーブルをストアドプロシージャに渡すにはどうすればよいですか?

ストアドプロシージャをモデルに追加しようとしましたが、更新されたコンテキストで目的のストアドプロシージャを見つけることができません。

私がやろうとしているのは、テーブルへの一括挿入を実行することです。これが現在使用しているメソッドです。

List<items> itemToInsertToDB = //fetchItems;

foreach(items i in itemToInsertToDB)
{
     context.sp_InsertToTable(i.ColumnA, i.ColumnB)
}

現在、私はforeachループを使用してリストをループし、アイテムをDBに挿入していますが、リストにアイテムが多数ある場合はパフォーマンスの問題が発生するため、ストアドプロシージャにリストし、内部に挿入します。

では、この問題をどのように解決するのでしょうか?またはこれを行うためのより良い方法はありますか?

13
User2012384

GUIDの単一の列を持つテーブルを送信するとします。

まず、テーブル(列)のスキーマを表す SqlMetaData を使用して構造を作成する必要があります。

var tableSchema = new List<SqlMetaData>(1)
{
  new SqlMetaData("Id", SqlDbType.UniqueIdentifier)
}.ToArray();

次に、 SqlDataRecord を使用して、スキーマに一致するレコードのリストを作成します。

var tableRow = new SqlDataRecord(tableSchema);
tableRow.SetGuid(0, Guid.NewGuid());
var table = new List<SqlDataRecord>(1)
{
  tableRow
};

次に、 SqlParameter を作成します。

var parameter = new SqlParameter();
parameter.SqlDbType = SqlDbType.Structured;
parameter.ParameterName = "@UserIds";
parameter.Value = table;

var parameters = new SqlParameter[1]
{
  parameter
};

次に、 Database.SqlQuery を使用してストアドプロシージャを呼び出すだけです。

IEnumerable<ReturnType> result;
using (var myContext = new DbContext())
{
  result = myContext.Database.SqlQuery<User>("GetUsers", parameters)
    .ToList();         // calls the stored procedure
    // ToListAsync();  // Async
{

SQL Serverで、ユーザー定義のテーブルタイプを作成します(TTV、テーブルタイプ値の接尾辞を付けます)。

CREATE TYPE [dbo].[UniqueidentifiersTTV] AS TABLE(
  [Id] [uniqueidentifier] NOT NULL
)
GO

次に、タイプをパラメーターとして指定します(テーブルタイプの値は読み取り専用である必要があることを忘れないでください!):

CREATE PROCEDURE [dbo].[GetUsers] (
  @UserIds [UniqueidentifiersTTV] READONLY
) AS
BEGIN
  SET NOCOUNT ON

  SELECT u.* -- Just an example :P
  FROM [dbo].[Users] u
  INNER JOIN @UserIds ids On u.Id = ids.Id
END
13
Erik Philips

ストアドプロシージャを使用してバルクデータを挿入するのではなく、EntityFrameworkの挿入メカニズムに依存することをお勧めします。

List<items> itemToInsertToDB = //fetchItems;
foreach(items i in itemToInsertToDB)
{
    TestType t = new TestType() { ColumnA = i.ColumnA, ColumnB = i.ColumnB };
    context.TestTypes.Add(t);
}
context.SaveChanges();

エンティティフレームワークは、これらの挿入を単一のトランザクションで、および(通常は)単一のクエリ実行でスマートに実行します。これは、ストアドプロシージャの実行とほぼ同じです。これは、大量のデータを挿入するためだけにストアドプロシージャに依存するよりも優れています。

3
Gilang