Сценарий, который вы описываете, задуман. Я тестировал как .NET 3.5, так и .NET 4.0 Beta 2 и получил одинаковые результаты. С учетом того, что SPROC использует структуру IF / ELSE, как ваша, генерируемые результаты и используемые инструменты:
- SqlMetal : IMultipleResults
- LINQ To SQL Designer (перетаскивание в IDE VS): ISingleResult
Это , поддерживаемый Мэттом Уорреном в Microsoft:
Дизайнер не распознает сохраненные
Procs с несколькими возвращаемыми значениями и
сопоставит их всех с возвращением
одно целое число
Инструмент командной строки SQLMetal делает
распознать множественные результаты и
напечатает возврат метода
правильно как IMultipleResults. Вы
можно использовать SQLMetal или изменить
DBML от руки или добавить метод
подпись для этого хранимого процесса на ваш
собственный частичный класс для вашего
DataContext.
В этом сообщении в блоге Динеш Кулькарни комментирует противоположный сценарий, когда дизайнер не добавляет IMultipleResults и вместо этого использует ISingleResult. Он заявляет (выделение добавлено):
И нет, дизайнер не поддерживает
эта особенность. Таким образом, вы должны добавить
метод в вашем частичном классе. SqlMetal
однако извлекает sproc. The
Причиной этого является реализация
подробно: оба используют один и тот же код
генератор, но другая база данных
схемы извлечения.
Кроме того, в разделе «Обработка нескольких форм результатов из 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
Очевидно, что типы должны быть похожими или что-то, что может быть приведено к другому. Например, если один из них был INT
, а другой - 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
и 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 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;
}
}
}
}
Вам нужно переименовать OrderId
в FakeId
и _OrderId
в _FakeId
. Как только это будет сделано, вы можете использовать ISingleResult выше, как обычно, например:
int fakeId = dc.FakeODIds(i).Single().FakeId;
На этом заканчивается то, что я использовал и смог найти по теме.