6

こんにちは、パラメータに応じて常に単一の行を返すストアド プロシージャがあります。

IF @bleh = 1
  SELECT TOP 1 Xyz FROM Abc
ELSE
  SELECT TOP 1 Def FROM Abc

SqlMetal を使用して DataContext を生成する必要がありますが、このストアド プロシージャはIMultipleResultsエラーである を返します。ISingleResult代わりに...を返す必要があります

if を削除すると (単一のSELECT呼び出しを配置する)、ISingleResult戻り値の型が生成されます。

何か案は?

4

1 に答える 1

10

説明しているシナリオは仕様によるものです。.NET3.5と.NET4.0Beta 2の両方でテストしたところ、同じ結果が得られました。あなたのようにIF/ELSE構造を使用するSPROCを考えると、生成される結果と使用されるツールは次のとおりです。

  • SqlMetalIMultipleResults
  • 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を返します。ProductIdFakeId

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

あなたがする必要があるのは、名前OrderIdFakeId_OrderIdに変更すること_FakeIdです。それが完了したら、通常どおりに上記のISingleResultを使用できます。次に例を示します。

int fakeId = dc.FakeODIds(i).Single().FakeId;

これで、私が使用し、トピックで見つけることができたものは終わりです。

于 2010-01-30T02:54:09.890 に答える