Требуемый столбец отсутствовал в результатах операции FromSql:. FromSqlInterpolated

[Pre-Amble]

Когда я обновил свой api с.netcore2.1 до.netcore3.1, я обнаружил, что мне нужно значительно изменить код Entity Framework.

Чего я не понимал, так это того, что у меня была ошибка в хранимой процедуре в моей удаленной базе данных, которая не выражалась в моем коде.netcore2.1.

Сообщения об ошибках, с которыми я столкнулся, не указали мне в направлении проверки внутри хранимой процедуры. Я оставляю окончательную форму своего вопроса на случай, если поиск сообщений об ошибках кому-то поможет.

[Вопрос]

У меня есть следующий код для вызова хранимой процедуры, которая возвращает целое число

Код работает в модульном тесте на стороне сервера, но не работает, когда он вызывается через api или через SwaggerUI.

var obj = connect.Ints.FromSqlInterpolated<intDto>(@$"Set NOCOUNT ON
          declare @num int 
          exec @num = spGetDefaultID {userName}  
          select @num as num").ToList();

id = (int)obj.First().num;

Где connect - это мой DbContext, содержащий

public DbSet<intDto> Ints { get; set; }  // I actually don't want a table

с участием

public class intDto
{
    public int num { get; set; }
}

Я последовал совету создать фиктивное представление, чтобы в базе данных не появлялась ненужная таблица.

и имеют

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<intDto>).HasNoKey().ToView("view_name_that_doesnt_exist");

Стек вызовов

System.InvalidOperationException: The required column 'num' was not present in the results of a 'FromSql' operation.
at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.BufferedDataRecord.InitializeFields()
at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.BufferedDataRecord.Initialize(DbDataReader reader, IReadOnlyList`1 columns)
at Microsoft.EntityFrameworkCore.Query.Internal.BufferedDataReader.Initialize(IReadOnlyList`1 columns) 
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.InitializeReader(DbContext _, Boolean result)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementation[TState,TResult](Func`3 operation, Func`3 verifySucceeded, TState state)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)   
at MyAPi.Job_Management.JobDataRead.GetCartIdForUser(ApiDbContext connect, String userName) 

Обратите внимание, я сильно отредактировал этот вопрос, потому что в какой-то момент у меня работал api, но больше не работает.

Я обнаружил, что мне нужно использовать.ToList() или я получил ошибку

System.InvalidOperationException: FromSqlRaw или FromSqlInterpolated были вызваны с несоставным SQL и с запросом, составляющим его. Рассмотрите возможность звонкаAsEnumerable после метода FromSqlRaw или FromSqlInterpolated для выполнения композиции на стороне клиента.

[Обновить]

Я ввел следующий код, и он не потерпел неудачу

 var obj = db.Ints.FromSqlInterpolated(@$"Set NOCOUNT on
                                       select count(*) as num from people 
                                       where email like  {name} ").ToList();

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

Или, может быть, это версия базы данных..

Интересно, что модульный тест не проходит, если у меня нет

Microsoft.EntityFrameworkCore.Relational.dll

упоминается в тестируемом проекте. Хотя я не использую это явно.

Сообщение об ошибке в этом сценарии:

System.MissingMethodException: 'Method not found:'System.Linq.IQueriable '1<!!O> 
Microsoft.EntityFrameworkCore.RelationalQueryableExtensions.
FromSqlInterpolated(Microsoft.EntityFrameworkCOre.DbSet'1<!!0>, 
System.FormattableString)'

Я попытался изменить свой код на

var obj = connect.Ints.FromSqlInterpolated<intDto>(@$"declare @num int 
          exec @num = spGetDefaultID {userName}").ToList();

Но мой модульный тест дал ошибку

The underlying reader doesn't have as many fields as expected

[Обновить]

Удаленная хранимая процедура возвращает данные с использованием набора результатов.

create PROCEDURE [dbo].[spGetDefaultID] 
@email varchar(300)
AS
BEGIN
    SET NOCOUNT ON;
    declare @cartID int
    select @CartID= id from people where email = @email   /* simplified */
    select @CartID /* this line was not present in my local database */
    return @CartID  
END

2 ответа

Решение

Когда я попытался вызвать хранимую процедуру с помощью ssms, как было предложено Zohar, я был подавлен, чтобы увидеть 2 вывода результатов

Когда я удалил ненужное

выберите @CartID

проблема была решена.

1. Вам необходимо изменить хранимую процедуру. Вам нужен выходной параметр. Это должно выглядеть так. нравится:

CREATE PROCEDURE [dbo].[spGetDefaultID] (
                 @Username VARCHAR(100)
                ,@num_output int OUTPUT)
AS 
BEGIN
  SELECT @num_output = count(*)
  FROM [Person].[Person]
  WHERE PersonType LIKE (@Username)
RETURN
END

2. Вам нужно изменить одну строку вашего кода. Синтаксис немного отличается от того, который вы выбрали. Вам нужен оператор OUTPUT.

SET NOCOUNT ON
DECLARE @num INT
exec spGetDefaultID {UserName}, @num_output=@num OUTPUT;
SELECT @num AS num

Возможно, вас сбило с толку то, что SQL возвращает значение, даже если вы пишете его, как в начале, но он не сохраняет результат в параметре @num.

Надеюсь, это решит вашу проблему. Дополнительную информацию о том, как вернуть данные из хранимой процедуры, можно найти в Microsoft Docs.

Другие вопросы по тегам