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

您描述的场景是设计使然。我已经使用 .NET 3.5 和 .NET 4.0 Beta 2 进行了测试,得到了相同的结果。给定一个使用 IF/ELSE 结构的 SPROC,生成的结果和使用的工具是:

  • SqlMetalIMultipleResults
  • LINQ To SQL 设计器(在 VS IDE 中拖放):ISingleResult

这得到了Microsoft的 Matt Warren的支持:

设计人员无法识别具有多个返回值的存储过程,并将它们全部映射为返回单个整数。

SQLMetal 命令行工具确实可以识别多个结果,并将方法的返回正确地键入为 IMultipleResults。您可以使用 SQLMetal 或手动修改 DBML,或者将此存储过程的方法签名添加到您自己的 DataContext 分部类中。

这篇博文中, Dinesh Kulkarni 评论了设计师不添加 IMultipleResults 而是使用 ISingleResult 的相反场景。他说(强调补充):

不,设计师不支持此功能。所以你必须在你的部分类中添加方法。然而,SqlMetal 确实提取了存储过程。原因在于实现细节:两者使用相同的代码生成器,但使用不同的数据库模式提取器。

此外,Scott Gu 的帖子中标题为“处理来自 SPROC 的多个结果形状”的部分和这篇 MSDN 文章都显示了 IMultipleResults 与使用相同结构的 SPROC 一起使用。

太好了,现在呢?有一些解决方法,有些比其他更好。


重写 SPROC

您可以重写 SPROC,以便 SqlMetal 使用 ISingleResult 生成函数。这可以通过

重写 #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

显然,类型需要相似或可以转换为其他类型。例如,如果一个是 an INT,另一个是 a DECIMAL(8, 2),您将使用小数来保持精度。

重写 #2 - 使用 case 语句:

这与马克的建议相同。

SELECT TOP 1 CASE WHEN @Input = 1 THEN OrderId ELSE ProductId END FROM OrderDetails

使用 UDF 而不是 SPROC

您可以使用标量值 UDF并调整您的查询以使用 UDF 格式(与上面提到的变量方法相同)。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返回和ProductIdas 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 别名,我们需要调整生成的代码。如果您导航到在步骤 2 中为您生成的映射类(FakeODIdsResult在我的情况下)。就我而言,该类将使用代码OrderId中步骤 1 中的原始列名。事实上,如果步骤 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和。完成后,您可以像往常一样使用上面的 ISingleResult,例如:_OrderId_FakeId

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

这总结了我使用过的内容并且能够找到关于该主题的内容。

于 2010-01-30T02:54:09.890 回答