Требуемый столбец не присутствовал в результатах операции «От Sql»:. FromSqlInterpolated - PullRequest
0 голосов
/ 22 марта 2020

[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 для выполнения композиции на стороне клиента.

[Update]

Я вставил следующий код, и он не вышел из строя

 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 ]

2 голосов
/ 02 апреля 2020

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

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

select @ CartID

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

0 голосов
/ 02 апреля 2020

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.

...