SqlMetal 错误地生成我的存储过程的返回类型 (LINQ)
您好,有一个存储过程,它总是根据参数返回一行:
IF @bleh = 1
SELECT TOP 1 Xyz FROM Abc
ELSE
SELECT TOP 1 Def FROM Abc
我必须使用 SqlMetal 生成 DataContext,但此存储过程返回一个 IMultipleResults
,这是一个错误。相反,它应该返回一个 ISingleResult
...
如果我删除 if(进行单个 SELECT
调用),则会生成一个 ISingleResult
返回类型。
有什么想法吗?
Hi have a stored proc that always returns a single row depending of a parameter:
IF @bleh = 1
SELECT TOP 1 Xyz FROM Abc
ELSE
SELECT TOP 1 Def FROM Abc
I must use SqlMetal to generate the DataContext but this stored procedure returns a IMultipleResults
, which is an error. Instead it should return a ISingleResult
...
If I remove the if (putting a single SELECT
call), an ISingleResult
return type is generated.
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您所描述的场景是设计使然。我已经使用 .NET 3.5 和 .NET 4.0 Beta 2 进行了测试,并得到了相同的结果。给定一个像您一样使用 IF/ELSE 结构的 SPROC,生成的结果和使用的工具为:
这是 由 Microsoft 的 Matt Warren 支持:
在 这篇博文中 Dinesh Kulkarni 评论了相反的情况,即设计者不添加 IMultipleResults 而是使用 ISingleResult。他指出(强调是后加的):
此外,Scott Gu 的帖子 和 这篇 MSDN 文章 都显示 IMultipleResults 与使用相同结构的 SPROC 一起使用。
太好了,现在怎么办?有一些解决方法,其中一些比其他更好。
重写 SPROC
您可以重写 SPROC,以便 SqlMetal 使用 ISingleResult 生成函数。这可以通过
重写 #1 - 将结果存储在变量中来实现:
显然,类型需要相似或可以转换为其他类型。例如,如果一个是
INT
,另一个是DECIMAL(8, 2)
,您将使用小数来保留精度。重写 #2 - 使用 case 语句:
这与 马克的建议。
使用 UDF 而不是 SPROC
您可以使用 标量值 UDF 并调整您的查询以使用 UDF 格式(与上面提到的变量方法相同)。 SqlMetal 将为它生成一个 ISingleResult,因为只返回一个值。
伪造 SPROC 和将其关闭
这可行,但比以前的选项更乏味。此外,将来使用 SqlMetal 将覆盖这些更改并需要重复该过程。使用分部类并将相关代码移动到那里将有助于防止这种情况。
1) 更改您的 SPROC 以返回单个
SELECT
语句(注释掉您的实际代码),例如SELECT TOP 1 OrderId FROM OrderDetails
>2) 使用 SqlMetal。它将生成一个 ISingleResult:
3) 将您的 SPROC 更改回其原始形式,但对返回的结果使用相同的别名。例如,我会将
OrderId
和ProductId
都返回为FakeId
。请注意,我在这里没有使用变量,而是直接使用您最初开始使用的格式。
4) 由于我们使用 FakeId 别名,因此我们需要调整生成的代码。如果您导航到在步骤 2 中为您生成的映射类(在我的例子中为
FakeODIdsResult
)。该类将使用代码中步骤 1 中的原始列名称,在我的例子中为OrderId
。事实上,如果步骤 1 中的语句以别名开头,则可以避免这整个步骤,即:从 OrderDetails 中选择前 1 个 OrderId 作为 FakeId
。如果你没有,你需要进去并调整一些东西。FakeODIdsResult 将使用
OrderId
,由于它是FakeId
的别名,因此不会返回任何内容。它看起来类似于:您需要做的是将
OrderId
重命名为FakeId
,将_OrderId
重命名为_FakeId
。完成后,您可以像平常一样使用上面的 ISingleResult,例如:这总结了我在该主题上使用过并能够找到的内容。
The scenario you're describing is by design. I've tested with both .NET 3.5 and .NET 4.0 Beta 2 and got the same results. Given a SPROC using an IF/ELSE structure as yours does, the generated results and tools used are:
This is supported by Matt Warren at Microsoft:
In this blog post Dinesh Kulkarni comments on the opposite scenario where the designer doesn't add IMultipleResults and uses ISingleResult instead. He states (emphasis added):
In addition, the section titled "Handling Multiple Result Shapes from SPROCs" in Scott Gu's post and this MSDN article both show IMultipleResults being used with SPROCs that use the same structure.
Great, now what? There are a few workarounds, some are nicer than others.
Rewrite the SPROC
You can rewrite the SPROC so that SqlMetal generates the function using ISingleResult. This can be achieved by
Rewrite #1 - Storing the result in a variable:
Obviously the types will need to be similar or something that can be cast to the other. For example, if one was an
INT
and the other was aDECIMAL(8, 2)
you would use the decimal to retain precision.Rewrite #2 - Use a case statement:
This is identical to Mark's suggestion.
Use a UDF instead of a SPROC
You could use a scalar-valued UDF and adjust your query to use the UDF format (identical to the variable approach mentioned above). SqlMetal will generate an ISingleResult for it since only one value is returned.
Fake the SPROC & switch it out
This works but is more tedious than the previous options. Also, future use of SqlMetal would overwrite these changes and require the process to be repeated. Using a partial class and moving the relative code there would help prevent this.
1) Change your SPROC to return a single
SELECT
statement (comment out your actual code), such asSELECT TOP 1 OrderId FROM OrderDetails
2) Use SqlMetal. It will generate an ISingleResult:
3) Change your SPROC back to its original form but use the same alias for the returned result. For example, I will return both
OrderId
andProductId
asFakeId
.Notice I am not using a variable here but using the format you originally started with directly.
4) Since we're using the FakeId alias we need to tweak the generated code. If you navigate to the mapped class that was generated for you in step 2 (
FakeODIdsResult
in my case). The class will be using the original column name from step 1 in the code,OrderId
in my case. In fact, this whole step could be avoided if the statement in step 1 was aliased to start with, ie.SELECT TOP 1 OrderId As FakeId FROM OrderDetails
. If you didn't though, you need to go in and tweak things.FakeODIdsResult will be using
OrderId
, which will return nothing since it aliasesFakeId
. It will look similar to this:What you need to do is rename
OrderId
toFakeId
and_OrderId
to_FakeId
. Once that's done, you can use the ISingleResult above as you normally would, for example:This concludes what I've used and was able to find on the topic.