説明しているシナリオは仕様によるものです。.NET3.5と.NET4.0Beta 2の両方でテストしたところ、同じ結果が得られました。あなたのようにIF/ELSE構造を使用するSPROCを考えると、生成される結果と使用されるツールは次のとおりです。
- SqlMetal:IMultipleResults
- LINQ To SQL Designer(VS IDEにドラッグアンドドロップ):ISingleResult
これは、MicrosoftのMattWarrenによってサポートされています。
デザイナは、複数の戻り値を持つストアドプロシージャを認識せず、それらすべてを単一の整数を返すようにマップします。
SQLMetalコマンドラインツールは複数の結果を認識し、メソッドの戻り値をIMultipleResultsとして正しく入力します。SQLMetalを使用するか、DBMLを手動で変更するか、このストアドプロシージャのメソッドシグネチャをDataContextの独自の部分クラスに追加することができます。
このブログ投稿で、Dinesh Kulkarniは、デザイナーがIMultipleResultsを追加せず、代わりにISingleResultを使用する反対のシナリオについてコメントしています。彼は次のように述べています(強調を追加):
いいえ、設計者はこの機能をサポートしていません。したがって、部分クラスにメソッドを追加する必要があります。ただし、SqlMetalはsprocを抽出します。その理由は、実装の詳細です。2つは同じコードジェネレーターを使用しますが、データベーススキーマエクストラクターは異なります。
さらに、Scott Guの投稿の「SPROCからの複数の結果形状の処理」というタイトルのセクションとこのMSDNの記事は、どちらも同じ構造を使用するSPROCで使用されているIMultipleResultsを示しています。
素晴らしい、今何?いくつかの回避策があり、いくつかは他よりも優れています。
SPROCを書き直します
SqlMetalがISingleResultを使用して関数を生成するように、SPROCを書き直すことができます。これは、
書き直し#1-結果を変数に保存します:
DECLARE @Result INT
IF @Input = 1
SET @Result = (SELECT TOP 1 OrderId FROM OrderDetails)
ELSE
SET @Result = (SELECT TOP 1 ProductId FROM OrderDetails ORDER BY ProductId DESC)
SELECT @Result As Result
明らかに、タイプは類似しているか、他のタイプにキャストできるものである必要があります。たとえば、一方がINT
で、もう一方がであるDECIMAL(8, 2)
場合、精度を維持するために小数を使用します。
書き直し#2-caseステートメントを使用します。
これは、マークの提案と同じです。
SELECT TOP 1 CASE WHEN @Input = 1 THEN OrderId ELSE ProductId END FROM OrderDetails
SPROCの代わりにUDFを使用する
スカラー値のUDFを使用し、UDF形式を使用するようにクエリを調整できます(上記の変数アプローチと同じです)。返される値は1つだけなので、SqlMetalはそのISingleResultを生成します。
CREATE FUNCTION [dbo].[fnODIds]
(
@Input INT
)
RETURNS INT
AS
BEGIN
DECLARE @Result INT
IF @Input = 1
SET @Result = (SELECT TOP 1 UnitPrice FROM OrderDetails)
ELSE
SET @Result = (SELECT TOP 1 Quantity FROM OrderDetails ORDER BY Quantity DESC)
RETURN @Result
END
SPROCを偽造して切り替えます
これは機能しますが、以前のオプションよりも面倒です。また、将来SqlMetalを使用すると、これらの変更が上書きされ、プロセスを繰り返す必要があります。部分クラスを使用し、そこに相対コードを移動すると、これを防ぐのに役立ちます。
1) SPROCを変更して、次のような単一のSELECT
ステートメントを返すようにします(実際のコードをコメントアウトします)。SELECT TOP 1 OrderId FROM OrderDetails
2) SqlMetalを使用します。ISingleResultを生成します。
[Function(Name = "dbo.FakeODIds")]
public ISingleResult<FakeODIdsResult> FakeODIds([Parameter(Name = "Input", DbType = "Int")] System.Nullable<int> input)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), input);
return ((ISingleResult<FakeODIdsResult>)(result.ReturnValue));
}
3) SPROCを元の形式に戻しますが、返される結果には同じエイリアスを使用します。たとえば、との両方 OrderId
を返します。ProductId
FakeId
IF @Input = 1
SELECT TOP 1 OrderId As FakeId FROM OrderDetails
ELSE
SELECT TOP 1 Quantity As FakeId FROM OrderDetails ORDER BY Quantity DESC
ここでは変数を使用していませんが、最初に直接使用した形式を使用していることに注意してください。
4) FakeIdエイリアスを使用しているため、生成されたコードを微調整する必要があります。FakeODIdsResult
手順2(私の場合)で生成されたマップされたクラスに移動した場合。私の場合、クラスはコードのステップ1の元の列名を使用しますOrderId
。実際、ステップ1のステートメントが最初にエイリアス化されている場合、このステップ全体を回避できます。SELECT TOP 1 OrderId As FakeId FROM OrderDetails
。そうしなかった場合は、入って微調整する必要があります。
FakeODIdsResultはを使用します。OrderId
これはエイリアスであるため何も返しませんFakeId
。これは次のようになります。
public partial class FakeODIdsResult
{
private System.Nullable<int> _OrderId;
public FakeODIdsResult()
{
}
[Column(Storage = "_OrderId", DbType = "Int")]
public System.Nullable<int> OrderId
{
get
{
return this._OrderId;
}
set
{
if ((this._OrderId != value))
{
this._OrderId = value;
}
}
}
}
あなたがする必要があるのは、名前OrderId
をFakeId
と_OrderId
に変更すること_FakeId
です。それが完了したら、通常どおりに上記のISingleResultを使用できます。次に例を示します。
int fakeId = dc.FakeODIds(i).Single().FakeId;
これで、私が使用し、トピックで見つけることができたものは終わりです。