在 EF Core 中执行 SP

Executing an SP in EF Core

我正在尝试在 ef 核心中执行 sp。

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 结果定义了一个数据模型 class。

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\s\Ix.NET\Source\System.Interactive.Async\Select.cs:line 106
   at System.Linq.AsyncEnumerable.AsyncIterator`1.MoveNext(CancellationToken cancellationToken) in D:\a\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\s\Ix.NET\Source\System.Interactive.Async\Select.cs:line 106
   at System.Linq.AsyncEnumerable.AsyncIterator`1.MoveNext(CancellationToken cancellationToken) in D:\a\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 的信息都暗示它会比这更容易。

下面是我在 .net core 中调用存储过程的方式

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 调用到对象的映射是否正常,然后添加另一个 variable/parameter.

问题出在你的模型数据上 Annotation.If 你想为列指定一个确切的数据类型,你需要使用 [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