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?
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.