Выполнение SP в EF Core

Я пытаюсь выполнить sp в ядре ef.

ALTER PROCEDURE [dbo].[usp_get_counts_for_event_type_for_single_date]
    @OrgCode varchar(5),
    @ProcessDate date
AS
BEGIN
    SET NOCOUNT ON

    DECLARE @StartTime time = '00:00:00'
    DECLARE @EndTime time = '23:59:59'

    DECLARE @PeriodStart datetime = CONVERT(datetime, @ProcessDate) + CONVERT(datetime, @StartTime)
    DECLARE @PeriodEnd datetime = CONVERT(datetime, @ProcessDate) + CONVERT(datetime, @EndTime)

    -- Insert statements for procedure here
    SELECT CONVERT(VARCHAR(100), et.DisplayName) as Title, 
            et.DashboardColour as Colour,
            count(et.EventTypeId) as Count
    from EventType et
    join EventLog el on et.EventTypeId = el.EventTypeId
    WHERE el.StartTime BETWEEN @PeriodStart AND @PeriodEnd
    group by et.DisplayName, et.DashboardColour
END

Я определил класс модели данных для результатов sp.

public class GroupedCountResult
{
    [Key]
    [Column("varchar(100)")]
    public string Title { get; set; }

    [Column("varchar(20)")]
    public string Colour { get; set; }

    [Required]
    public int Count { get; set; }
}

А затем в DbContext.cs он был добавлен как DbSet в контексте

public  DbSet<GroupedCountResult> GroupedCountResults { get; set; }

Затем я пытаюсь выполнить его в контроллере Core 2.2 MVC. Это мое слабое место. Я пытаюсь разбить результаты на строки, которые затем используются в javascript для круговой диаграммы. Жестко закодированные значения, пока я не заработаю.

public async Task<ActionResult> Index()
{
    StringBuilder sbLabels = new StringBuilder();
    StringBuilder sbColours = new StringBuilder();
    StringBuilder sbCounts = new StringBuilder();
    string OrgCode = "HVO";
    DateTime ProcessDate = new DateTime(2019, 08, 01); //.ToString("yyyyMMdd");

    IEnumerable<GroupedCountResult> results =  await _context.GroupedCountResults
                                .FromSql($"usp_get_counts_for_event_type_for_single_date @p0, @p1", OrgCode, ProcessDate)
                                .ToListAsync();

    foreach(GroupedCountResult result in results) <--exception here
    {
        sbLabels.AppendFormat("'{0}',", result.Title);
        sbColours.AppendFormat("'{0}',", result.Colour);
        sbCounts.AppendFormat("{0},", result.Count);
    }

    ViewBag.Labels = sbLabels.ToString().TrimEnd(',');
    ViewBag.Colours = sbColours.ToString().TrimEnd(',');
    ViewBag.Counts = sbCounts.ToString().TrimEnd(',');
    return View();
}

Из журналов. Я предпочел версию с интерполяцией строк, но она кажется более эффективной.

Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (31ms) [Parameters=[@p0='?' (Size = 4000), @p1='?' (DbType = DateTime2)], CommandType='Text', CommandTimeout='30']
usp_get_counts_for_event_type_for_single_date @p0, @p1

Я работал с несколькими исключениями. Теперь он генерирует исключение после выполнения, что означает, что с результатами что-то не так.

Microsoft.EntityFrameworkCore.Query:Error: An exception occurred while iterating over the results of a query for context type 'Ctrack.Dashboard.Data.DbContext'.
System.InvalidOperationException: The required column 'varchar(100)' was not present in the results of a 'FromSql' operation.
   at Microsoft.EntityFrameworkCore.Query.Sql.Internal.FromSqlNonComposedQuerySqlGenerator.CreateValueBufferFactory(IRelationalValueBufferFactoryFactory relationalValueBufferFactoryFactory, DbDataReader dataReader)
   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.<NotifyReaderCreated>b__14_0(FactoryAndReader s)
   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.NotifyReaderCreated(DbDataReader dataReader)
   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken)
   at System.Linq.AsyncEnumerable.SelectEnumerableAsyncIterator`2.MoveNextCore(CancellationToken cancellationToken) in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\Select.cs:line 106
   at System.Linq.AsyncEnumerable.AsyncIterator`1.MoveNext(CancellationToken cancellationToken) in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\AsyncIterator.cs:line 98
   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)


System.InvalidOperationException: The required column 'varchar(100)' was not present in the results of a 'FromSql' operation.
   at Microsoft.EntityFrameworkCore.Query.Sql.Internal.FromSqlNonComposedQuerySqlGenerator.CreateValueBufferFactory(IRelationalValueBufferFactoryFactory relationalValueBufferFactoryFactory, DbDataReader dataReader)
   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.<NotifyReaderCreated>b__14_0(FactoryAndReader s)
   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.NotifyReaderCreated(DbDataReader dataReader)
   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken)
   at System.Linq.AsyncEnumerable.SelectEnumerableAsyncIterator`2.MoveNextCore(CancellationToken cancellationToken) in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\Select.cs:line 106
   at System.Linq.AsyncEnumerable.AsyncIterator`1.MoveNext(CancellationToken cancellationToken) in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\AsyncIterator.cs:line 98
   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)
Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action Ctrack.Dashboard.Controllers.HomeController.Index (Ctrack.Dashboard) in 1375.9283ms
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executed endpoint 'Ctrack.Dashboard.Controllers.HomeController.Index (Ctrack.Dashboard)'
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware:Error: An unhandled exception has occurred while executing the request.

Он потерял имя столбца? Я попытался преобразовать столбец в varchar (100). Вся информация о выполнении sp подразумевала, что это будет проще, чем это.


person Hecatonchires    schedule 07.08.2019    source источник


Ответы (2)


Проблема заключается в аннотации данных вашей модели. Если вы хотите указать точный тип данных для столбца, вам нужно использовать [Column(TypeName = "varchar(100)")] вместо [Column("varchar(200)")], который будет отображать имя столбца, а не тип столбца.

public class GroupedCountResult
{
    [Key]
    [Column(TypeName ="varchar(100)")]
    public int Title { get; set; }

    [Column(TypeName = "varchar(20)")]
    public string Colour { get; set; }
}

Ссылаться на

https://docs.microsoft.com/en-us/ef/core/modeling/relational/data-types

https://docs.microsoft.com/en-us/ef/core/modeling/relational/columns

person Ryan    schedule 07.08.2019

Вот как я вызываю свою хранимую процедуру в ядре .net

public class DbUtilities
{
    private readonly ILogHandler _oisLogger;
    private readonly SB1_VVFContext _context;

    public DbUtilities(ILogHandler oislogger, SB1_VVFContext context)
    {
        _oisLogger = oislogger;
        _context = context;
    }

    public IEnumerable<VehicleSearchResultDTO> SearchVehicleRecords(VehicleSearchCriteriaDTO Criteria)
    {
        List<VehicleSearchResultDTO> result = new List<VehicleSearchResultDTO>();
        VehicleSearchResultDTO vehRec = null;

        try
        {
            //Define Command Object
            _context.Database.OpenConnection();
            DbCommand cmd = _context.Database.GetDbConnection().CreateCommand();

            SqlParameter refnum = new SqlParameter("@RefNum", SqlDbType.VarChar, 10)
            {
                Direction = ParameterDirection.Input,
                Value = (!string.IsNullOrWhiteSpace(Criteria.RefNum)) ? Criteria.RefNum : string.Empty
            };

            //Define Input parameters
            SqlParameter vin = new SqlParameter("@Vin", SqlDbType.VarChar, 100)
            {
                Direction = ParameterDirection.Input,
                Value = (!string.IsNullOrWhiteSpace(Criteria.Vin)) ? Criteria.Vin : string.Empty
            };

            SqlParameter owner = new SqlParameter("@Owner", SqlDbType.VarChar, 200)
            {
                Direction = ParameterDirection.Input,
                Value = (!string.IsNullOrWhiteSpace(Criteria.RegisteredOwner)) ? Criteria.RegisteredOwner : string.Empty
            };

            SqlParameter plate = new SqlParameter("@Plate", SqlDbType.VarChar, 10)
            {
                Direction = ParameterDirection.Input,
                Value = (!string.IsNullOrWhiteSpace(Criteria.Plate)) ? Criteria.Plate : string.Empty
            };

            SqlParameter email = new SqlParameter("@Email", SqlDbType.VarChar, 200)
            {
                Direction = ParameterDirection.Input,
                Value = (!string.IsNullOrWhiteSpace(Criteria.Email)) ? Criteria.Email : string.Empty
            };

            SqlParameter VehicleReviewStatusId = new SqlParameter("@VehicleReviewStatusId", SqlDbType.Int)
            {
                Direction = ParameterDirection.Input,
                Value = Criteria.VehicleReviewStatusId
            };

            //Define properties for command object and execute the SP
            cmd.CommandText = "usp_Vehicle_Search_Result";
            cmd.Parameters.Add(refnum);
            cmd.Parameters.Add(vin);
            cmd.Parameters.Add(owner);
            cmd.Parameters.Add(plate);
            cmd.Parameters.Add(email);
            cmd.Parameters.Add(VehicleReviewStatusId);
            cmd.CommandTimeout = 10;
            cmd.CommandType = CommandType.StoredProcedure;

            DbDataReader reader = cmd.ExecuteReader();

            if (reader.HasRows)
            {
                //Get Search Result
                while (reader.Read())
                {
                    vehRec = new VehicleSearchResultDTO
                    {
                        VehicleID = (Guid)reader["VehicleId"],
                        ReferenceNumber = reader["ReferenceNumber"].ToString(),
                        SubmitterEmail = reader["EmailAddress"].ToString(),
                        Status = reader["StatusName"].ToString(),
                        SubmittedDate = CheckNull<DateTime?>(reader["DateSubmitted"]),
                        VIN = reader["VIN"].ToString(),
                        Year = CheckNull<int>(reader["Year"]),
                        Make = reader["Make"].ToString(),
                        LicensePlate = reader["LicensePlate"].ToString(),
                        ROName = reader["ROName"].ToString(),
                        StatusColor = reader["StatusColor"].ToString()
                    };
                    result.Add(vehRec);
                }
            }
            else
            {
                result = null;
            }
        }
        catch (Exception ex)
        {
            _oisLogger.LogError(2205, $"ERROR SearchVehicleRecords", ex);
        }
        finally
        {
            _context.Database.CloseConnection();
        }
        return result;
    }

Вот входной объект:

public class VehicleSearchCriteriaDTO
{
    public VehicleSearchCriteriaDTO()
    {
    }
    [Display(Name = "Ref#")]
    public string RefNum { get; set; }

    [Display(Name = "VIN")]
    public string Vin { get; set; }

    [Display(Name = "Reg Owner")]
    public string RegisteredOwner { get; set; }

    [Display(Name = "Plate")]
    public string Plate { get; set; }

    [Display(Name = "Email")]
    public string Email { get; set; }

    public int VehicleReviewStatusId { get; set; }
}

Вот объект вывода:

 public class VehicleSearchResultDTO
{
    [Display(Name = "Owner")]
    public string ROName { get; set; }

    [Display(Name = "RefNum")]
    public string ReferenceNumber { get; set; }

    [Display(Name = "VIN")]
    public string VIN { get; set; }

    [Display(Name = "Plate")]
    public string LicensePlate { get; set; }

    [Display(Name = "Year")]
    public int Year { get; set; }

    [Display(Name = "Make")]
    public string Make { get; set; }

    [Display(Name = "Submitter Email")]
    public string SubmitterEmail { get; set; }

    [Display(Name = "Submitted Date")]
    public DateTime? SubmittedDate { get; set; }

    [Display(Name = "Status")]
    public string Status { get; set; }

    public Guid VehicleID { get; set; }
    public string StatusColor { get; set; }
}

Я не добавлял эти классы в dbcontext или proc в dbcontext. Кроме того, я бы посоветовал вам прокомментировать код, чтобы за один раз извлекать только одну результирующую переменную. Посмотрите, хорошо ли работает сопоставление с объектом из вызова SP, затем добавьте еще одну переменную/параметр.

person sam    schedule 07.08.2019