いくつかの動的SQLでいくつかのリンクサーバーを呼び出すストアドプロシージャにクエリがあります。 EFはそれが気に入らないことを理解しているため、返されるすべての列を具体的にリストしました。それでも、それはまだ好きではありません。ここで何が間違っていますか?必要なクラスを作成できるように、EFがストアドプロシージャから返された列を検出できるようにするだけです。
ストアドプロシージャの最後の行を構成する次のコードを参照してください。
SELECT
#TempMain.ID,
#TempMain.Class_Data,
#TempMain.Web_Store_Class1,
#TempMain.Web_Store_Class2,
#TempMain.Web_Store_Status,
#TempMain.Cur_1pc_Cat51_Price,
#TempMain.Cur_1pc_Cat52_Price,
#TempMain.Cur_1pc_Cat61_Price,
#TempMain.Cur_1pc_Cat62_Price,
#TempMain.Cur_1pc_Cat63_Price,
#TempMain.Flat_Length,
#TempMain.Flat_Width,
#TempMain.Item_Height,
#TempMain.Item_Weight,
#TempMain.Um,
#TempMain.Lead_Time_Code,
#TempMain.Wp_Image_Nme,
#TempMain.Wp_Mod_Dte,
#TempMain.Catalog_Price_Chg_Dt,
#TempMain.Description,
#TempMain.Supersede_Ctl,
#TempMain.Supersede_Pn,
TempDesc.Cust_Desc,
TempMfgr.Mfgr_Item_Nbr,
TempMfgr.Mfgr_Name,
TempMfgr.Vendor_ID
FROM
#TempMain
LEFT JOIN TempDesc ON #TempMain.ID = TempDesc.ID
LEFT JOIN TempMfgr ON #TempMain.ID = TempMfgr.ID
EFは、以下から結果セットを構築するストアドプロシージャのインポートをサポートしていません。
理由は、プロシージャをインポートするためですEFはそれを実行する必要があります。このような操作は、データベースの一部の変更をトリガーする可能性があるため危険です。そのため、EFはストアドプロシージャを実行する前に特別なSQLコマンドを使用します。
SET FMTONLY ON
このコマンドを実行すると、ストアドプロシージャは結果セットの列に関する「メタデータ」のみを返し、ロジックを実行しません。ただし、ロジックは実行されなかったため、一時テーブル(または構築された動的クエリ)はないため、メタデータには何も含まれていません。
次の2つの選択肢があります(これらの機能を使用しないためにストアドプロシージャを書き直す必要がある場合を除く)。
SET FMTONLY OFF
。これにより、SPの残りのコードを通常の方法で実行できます。インポート中にこれらの変更が実行されるため、SPはデータを変更しないことを確認してください!インポートが成功したら、そのハックを削除します。この非論理的なコードブロックを追加することで問題は解決しました。決してヒットしませんが
IF 1=0 BEGIN
SET FMTONLY OFF
END
または、ユーザー定義のテーブルタイプを作成して返すことができます。
CREATE TYPE T1 AS TABLE
( ID bigint NOT NULL
,Field1 varchar(max) COLLATE Latin1_General_CI_AI NOT NULL
,Field2 bit NOT NULL
,Field3 varchar(500) NOT NULL
);
GO
次に、手順で:
DECLARE @tempTable dbo.T1
INSERT @tempTable (ID, Field1, Field2, Field3)
SELECT .....
....
SELECT * FROM @tempTable
これで、EFは返された列の型を認識できるはずです。
他の一部が指摘しているように、手順が実際に実行されることを確認してください。特に、私の場合、管理者権限でログインしたことを完全に忘れて、SQL Server Management Studioでエラーなしで問題なくプロシージャを実行していました。アプリケーションのプリンシパルユーザーを使用してプロシージャを実行しようとすると、クエリにテーブルがあり、そのユーザーにはアクセス許可がありませんでした。
@tmanthleyが言ったことに加えて、SSMSで最初に実行して、ストアドプロシージャが実際に機能することを確認してください。いくつかのストアドプロシージャをインポートし、いくつかの依存するスカラー関数を忘れたため、EFはプロシージャが列を返さないと判断しました。私が以前に見つけたはずの間違いのように思えますが、その場合EFはエラーメッセージを表示しません。
私が追加するものは:
ストアドプロシージャにパラメーターがあり、既定のパラメーター値の結果セットが返されない場合、インポートも失敗します。
私のストアドプロシージャには2つの浮動小数点パラメーターがあり、両方のパラメーターが0の場合は何も返しません。
したがって、このストアドプロシージャをエンティティモデルに追加するには、これらのパラメータの値をストアドプロシージャに設定して、パラメータが実際に何であるかに関係なく、いくつかの行を返すようにします。
次に、このストアドプロシージャをエンティティモデルに追加した後、変更を元に戻しました。
両方の解決策:1-返された複合型を手動で定義します(動作するはずです)2-ハックを使用し、最初に設定したストアドプロシージャを追加するために、SET FMTONLY OFFを設定します。
いくつかの手順で私と働いていませんが、他の手順で働いていました!
私の手順は次の行で終了します。
SELECT machineId, production [AProduction]
, (select production FROM #ShiftBFinalProd WHERE machineId = #ShiftAFinalProd.machineId) [BProduction]
, (select production FROM #ShiftCFinalProd WHERE machineId = #ShiftAFinalProd.machineId) [CProduction]
FROM #ShiftAFinalProd
ORDER BY machineId
ありがとう
興味深い副注:一時テーブルではなくテーブル変数を使用して最初に解決したのと同じ問題がありました(インポートのみ)。それは私にとって特に直感的ではありませんでした。最初に私の2つのSProcを観察したとき、私は気を失いました。
(SET FMTONLY OFFは私には役に立たなかったので、ちょうどFYIとしてEF側のハックに煩わされるのではなく、列情報を取得するために一時的にSProcsを変更しました。)
私の最善のオプションは、実際には、単に手動で複合型を作成し、関数インポートをそれにマッピングすることでした。うまく機能し、唯一の違いは、プロパティを作成するための追加のFactoryMethodがDesignerに含まれていたことです。
SET FMTONLY OFF
いずれかの手順で私のために働いたが、他の手順では失敗した。手順に従うことで問題を解決できます
ストアドプロシージャ内で、同じ列タイプの一時テーブルを作成し、動的クエリによって返されたすべてのデータを一時テーブルに挿入しました。一時テーブルのデータを選択しました。
Create table #temp
(
-- columns with same types as dynamic query
)
EXEC sp_executeSQL @sql
insert into #temp
Select * from #temp
drop table #temp
古いストアドプロシージャの既存の複合型、インポート関数、ストアドプロシージャインスタンスを削除し、現在の新しいプロシージャのエンティティモデルを更新しました。
目的の複合型のエンティティモーダルでインポートされた関数を編集すると、以前のストアドプロシージャでは取得できない列情報がすべて取得されます。
型の作成が完了したら、ストアドプロシージャから一時テーブルを削除して、Entity Frameworkを更新できます。
Entityフレームワークでは、列情報を取得している間に、sqlはパラメーターにnull値を渡してプロシージャを実行します。そのため、必要なすべての列を含む一時テーブルを作成し、プロシージャにnullが渡されたときに値のないすべての列を返すことで、nullケースを異なる方法で処理しました。
私の手順では、次のような動的クエリがありました
declare @category_id int
set @category_id = (SELECT CATEGORY_ID FROM CORE_USER where USER_ID = @USER_ID)
declare @tableName varchar(15)
declare @sql VARCHAR(max)
declare @USER_IDT varchar(100)
declare @SESSION_IDT varchar(10)
IF (@category_id = 3)
set @tableName = 'STUD_STUDENT'
else if(@category_id = 4)
set @tableName = 'STUD_GUARDIAN'
if isnull(@tableName,'')<>''
begin
set @sql = 'SELECT [USER_ID], [FIRST_NAME], SCHOOL_NAME, SOCIETY_NAME, SCHOOL_ID,
SESSION_ID, [START_DATE], [END_DATE]
from @tableName
....
EXECUTE (@sql)
END
ELSE
BEGIN
SELECT * from #UserPrfTemp
END
設定されたFMTONLY OFFトリックを使用した後、私の場合は列情報を取得していませんでした。
これは、空のデータを取得するために作成した一時テーブルです。今、私は列情報を取得しています
Create table #UserPrfTemp
(
[USER_ID] bigint,
[FIRST_NAME] nvarchar(60),
SCHOOL_NAME nvarchar(60),
SOCIETY_NAME nvarchar(200)
.....
}
Entity Frameworkは、ストアドプロシージャを実行し、すべての引数にNULLを渡すことで列を取得しようとします。
ストアドプロシージャがすべての状況で何かを返すことを確認してください。 NULLではなく、Entity Frameworkが引数のデフォルト値を使用してストアドプロシージャを実行する方が賢い場合があることに注意してください。
ERは、テーブルのメタデータを取得するために次のことを行います。
FMTONLY ONを設定
これにより、特に一時テーブルを使用する場合など、さまざまな状況でストアドプロシージャが破損します。
したがって、結果を複合型として取得するには、追加してみてください
FMTONLY OFFに設定します。
これは私のために働いた-それもあなたのために働くことを願っています。
私の場合、SET FMTONLY OFFは機能しませんでした。私が従った方法は、元のストアドプロシージャのバックアップを取り、以下のクエリのように列名のみで置き換えることです。
Select Convert(max,'') as Id,Convert(max,'') as Name
この変更後、エンティティフレームワークに新しい関数インポート、複合型を作成します。関数インポートと複合型が作成されたら、上記のクエリを元のストアドプロシージャに置き換えます。
私の場合、プロシージャの先頭にSET NOCOUNT ON;
を追加すると問題が修正されました。とにかくベストプラクティスです。