SqlMetal неправильно генерирует тип возврата моего сохраненного proc (LINQ)

Привет имейте сохраненный 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 возвратитесь тип сгенерирован.

Какие-либо идеи?

6
задан Ahmad Mageed 30 January 2010 в 15:23
поделиться

1 ответ

Сценарий, который вы описываете, задуман. Я тестировал как .NET 3.5, так и .NET 4.0 Beta 2 и получил те же результаты. Для SPROC, использующего структуру IF / ELSE, как у вас, сгенерированные результаты и используемые инструменты:

  • SqlMetal : IMultipleResults
  • LINQ To SQL Designer (перетащите в VS IDE): ISingleResult

Это , поддерживаемый Мэттом Уорреном в Microsoft:

Разработчик не распознает сохраненные процессы с несколькими возвращаемыми значениями и сопоставит их все с возвращением единственного целого числа .

Инструмент командной строки SQLMetal распознает множественные результаты, а правильно набирает результат метода как IMultipleResults. Вы можете либо использовать SQLMetal, либо вручную изменить DBML, либо добавить сигнатуру метода для этой хранимой процедуры в свой частичный класс для вашего {{1 }} DataContext.

В этой записи блога Динеш Кулкарни комментирует противоположный сценарий, когда дизайнер не добавляет IMultipleResults и вместо этого использует ISingleResult. Он заявляет (выделено автором):

И нет, разработчик не поддерживает эту функцию. Поэтому вам нужно добавить метод в свой частичный класс. Однако SqlMetal извлекает sproc. Причиной является деталь реализации : оба используют один и тот же генератор кода , но разные экстракторы схемы базы данных .

Кроме того, раздел под названием «Обработка множественных форм результатов из SPROC» в посте Скотта Гу и в этой статье MSDN оба показывают, что IMultipleResults используется с SPROC, которые используют ту же структуру.

Отлично, что теперь? Есть несколько обходных путей, некоторые из них лучше, чем другие.


Перепишите 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 вместо 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 был назначен псевдоним для начала, т. Е. ВЫБРАТЬ 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;

Это завершает то, что я использовал и смог найти по теме.

10
ответ дан 10 December 2019 в 00:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: