Привет имейте сохраненный proc, который всегда возвращает одну строку в зависимости от параметра:
IF @bleh = 1
SELECT TOP 1 Xyz FROM Abc
ELSE
SELECT TOP 1 Def FROM Abc
Я должен использовать SqlMetal для генерации DataContext, но эта хранимая процедура возвращает a IMultipleResults
, который является ошибкой. Вместо этого это должно возвратить a ISingleResult
...
Если я удаляю если (помещение сингла SELECT
звоните), ISingleResult
возвратитесь тип сгенерирован.
Какие-либо идеи?
Сценарий, который вы описываете, задуман. Я тестировал как .NET 3.5, так и .NET 4.0 Beta 2 и получил те же результаты. Для SPROC, использующего структуру IF / ELSE, как у вас, сгенерированные результаты и используемые инструменты:
Это , поддерживаемый Мэттом Уорреном в Microsoft:
Разработчик не распознает сохраненные процессы с несколькими возвращаемыми значениями и сопоставит их все с возвращением единственного целого числа .
Инструмент командной строки SQLMetal распознает множественные результаты, а правильно набирает результат метода как IMultipleResults. Вы можете либо использовать SQLMetal, либо вручную изменить DBML, либо добавить сигнатуру метода для этой хранимой процедуры в свой частичный класс для вашего {{1 }} DataContext.
В этой записи блога Динеш Кулкарни комментирует противоположный сценарий, когда дизайнер не добавляет IMultipleResults и вместо этого использует ISingleResult. Он заявляет (выделено автором):
И нет, разработчик не поддерживает эту функцию. Поэтому вам нужно добавить метод в свой частичный класс. Однако SqlMetal извлекает sproc. Причиной является деталь реализации : оба используют один и тот же генератор кода , но разные экстракторы схемы базы данных .
Кроме того, раздел под названием «Обработка множественных форм результатов из SPROC» в посте Скотта Гу и в этой статье MSDN оба показывают, что IMultipleResults используется с SPROC, которые используют ту же структуру.
Отлично, что теперь? Есть несколько обходных путей, некоторые из них лучше, чем другие.
Вы можете переписать SPROC так, чтобы SqlMetal генерировал функцию с использованием ISingleResult. Это может быть достигнуто с помощью
Rewrite # 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 со скалярным значением и настроить свой запрос для использования формата 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
Это работает, но более утомительно, чем предыдущие варианты. Кроме того, использование 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 был назначен псевдоним для начала, т. Е. ВЫБРАТЬ 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;
Это завершает то, что я использовал и смог найти по теме.