IF @bleh = 1 SELECT TOP 1 Xyz FROM Abc ELSE SELECT TOP 1 Def FROM Abc
我必须使用sqlMetal生成DataContext,但此存储过程返回IMultipleResults,这是一个错误.相反它应该返回一个ISingleResult …
如果我删除if(放置一个SELECT调用),则会生成一个ISingleResult返回类型.
有任何想法吗?
解决方法
> sqlMetal:IMultipleResults
> LINQ to sql Designer(在VS IDE中拖放):ISingleResult
这是微软的supported by Matt Warren:
The designer does not recognize stored
procs with multiple return values and
will map them all to returning a
single integer.sqlMetal command line tool does
recognize the multiple results and
will type the return of the method
correctly as IMultipleResults. You
can either use sqlMetal or modify the
DBML by hand or add the method
signature for this stored proc to your
own partial class for your
DataContext.
在this blog post Dinesh Kulkarni评论了相反的情况,设计师没有添加IMultipleResults并使用ISingleResult.他说(重点补充):
And no,the designer does not support
this feature. So you have to add the
method in your partial class. sqlMetal
does however extract the sproc. The
reason for that is an implementation
detail: the two use the same code
generator but different database
schema extractors.
此外,Scott Gu’s post和this MSDN article中标题为“处理来自SPROC的多个结果形状”的部分都显示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
显然,类型需要相似或者可以投射到另一个类型.例如,如果一个是INT而另一个是DECIMAL(8,2),则使用小数来保持精度.
重写#2 – 使用案例陈述:
这与Mark’s suggestion相同.
SELECT TOP 1 CASE WHEN @Input = 1 THEN OrderId ELSE ProductId END FROM OrderDetails
使用UDF而不是SPROC
您可以使用scalar-valued 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和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别名,我们需要调整生成的代码.如果导航到在步骤2中为您生成的映射类(在我的情况下为FakeODIdsResult).该类将使用代码中步骤1的原始列名,在我的情况下使用OrderId.事实上,如果步骤1中的陈述是别名的,即,可以避免整个步骤,即. SELECT TOP 1 OrderId作为FakeId来自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;