0

在 .Net 5 Web Api 应用程序中,我正在从 MySql 表中读取一些行(使用 Pomelo 库)。我正在使用原始 sql 查询。所以:

MySqlParameter propertyIdParam = new MySqlParameter()
{
    ParameterName = "@propertyId",
    MySqlDbType = MySqlDbType.Int32,
    Direction = System.Data.ParameterDirection.Input,
    Value = 7
};

List<PropertyRepairsDto> propertyRepairs = await _context.PropertyRepairs
    .FromSqlRaw("some sql query", propertyIdParam)
    .AsNoTracking()
    .ToListAsync();

奇怪的是,这很好用,直到我提供一个不存在的参数(“propertyId”)——这样查询就不会返回任何行。在这种情况下,我得到以下异常:

System.InvalidCastException: Specified cast is not valid.
   at MySqlConnector.Core.Row.GetInt32(Int32 ordinal) in /_/src/MySqlConnector/Core/Row.cs:line 211
   at MySqlConnector.MySqlDataReader.GetInt32(Int32 ordinal) in /_/src/MySqlConnector/MySqlDataReader.cs:line 238
   at lambda_method848(Closure , QueryContext , DbDataReader , Int32[] )
   at Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at PropWorx.API.Controllers.PropertiesController.GetPropertyRepairsAsync(Int32 id) in C:\Users\fabsr\source\repos\PropWorx.API\PropWorx.API\Controllers\PropertiesController.cs:line 194
   at lambda_method479(Closure , Object )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at PropWorx.API.Middlewares.TenantIdentifier.Invoke(HttpContext httpContext, SharedContext sharedContext) in C:\Users\fabsr\source\repos\PropWorx.API\PropWorx.API\Middlewares\TenantIdentifier.cs:line 52
   at PropWorx.API.Middlewares.WebSocketsMiddleware.Invoke(HttpContext httpContext) in C:\Users\fabsr\source\repos\PropWorx.API\PropWorx.API\Middlewares\WebSocketsMiddleware.cs:line 28
   at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS
=======
Connection: keep-alive
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip, deflate, br
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1ERXpSRUUwUWpFMk9VTkdOa0kyUlRJMFJEUXdNakExUTBFeE1EUkNRalpDUmtWR1JVTXhOQSJ9.eyJpc3MiOiJodHRwczovL2NvZGV4Y3JlYXRpb25zLmV1LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHw1OTI2YjEwYTAxZTkxZDE0MGU5OWVlNjQiLCJhdWQiOlsiaHR0cHM6Ly9wcm9wd29yeC5jby56YS9hcGkiLCJodHRwczovL2NvZGV4Y3JlYXRpb25zLmV1LmF1dGgwLmNvbS91c2VyaW5mbyJdLCJpYXQiOjE2MTAwMDkyMjQsImV4cCI6MTYxMjYwMTIyNCwiYXpwIjoiUnppNzVkU1dpcVlFYlJsVjU1N0lFd1dQYUI5Qmwzd3giLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIn0.WK2gtsbWtiz1WIkJrO56Ap8fqyyyLBs1wiKcs0KvTVTPKlzVyicf6J9S-9PMDGs1uT-gho7fVW6gLVv9XzLCulcR4x3KFrNmLSW9lvsj9fyKPMQ4Udp4A2UdfYoogRXduB62QwqRhkYGbNkI-tSGgMwF-zgClirKalpY6oKs9yMY6qr_XC4ZzTI27Pd4W9wverLtLT0-1WNlh4ynUF_xE5uvfKfh0KhBITs-KASUBMz7WcloSehNqvnkwOOdLCcWtkcbcms4LRklwEzpQvXiuCwAgjpjq980fjFjYSISjD5paqBM_rdpvG4Yz4OqI6FXtccuszbc_1WcXe7O_CSuTg
Host: localhost:5001
Referer: http://localhost:5000/api/properties/6823/repairs
User-Agent: PostmanRuntime/7.26.8
Content-Length: 2856
clientId: 273
Postman-Token: fe2fcc49-3f1b-451b-8e06-b808fa28964c

我不明白当查询返回行时它如何工作正常(因此它将数据库数据正确映射到模型字段),但是当查询不返回任何行时(我认为这应该只是导致列表零项)它会引发异常。

该异常表明将值从数据库转换为 int32 时出现问题。起初,我认为它可能是一个空值,它试图映射到我的模型中的不可为空的整数,但我的模型中的所有整数字段都是可空的。但即便如此,由于查询没有返回任何行,所以它肯定不应该是一个问题,因为没有什么可以映射?

我知道如果没有看到数据库结构、模型、查询等,很难为我提供帮助。只是查询和表都很大,我想也许有人以前遇到过这个问题,在调用查询之前一切正常不返回任何行,在这种情况下会引发 System.InvalidCastException。

这是我要映射到的模型,因为它的价值:

public class PropertyRepairsDto
{
    public int? Id { get; set; }
    public int? FileId { get; set; }
    public string FileNum { get; set; }
    public bool? IsRequired { get; set; }
    public string Info { get; set; }
    public bool? QuotesObtained { get; set; }
    public decimal? QuoteAmount { get; set; }
    public string ContractorAppointed { get; set; }
    public bool? IsPaid { get; set; }
    public decimal? AmountPaid { get; set; }
    public DateTime? JobDate { get; set; }
    public bool? PaymentRequired { get; set; }
    public DateTime? PaymentDate { get; set; }
    public string PaymentReference { get; set; }
    public DateTime? ReportedAt { get; set; }
    public int? ContractorId { get; set; }
    public string Contractor { get; set; }
    public bool? IsFixedByOwner { get; set; }
    public DateTime? FollowUpDate { get; set; }
    public string WorkOrderNumber { get; set; }
    public bool? IsCompleted { get; set; }
    public DateTime? CompletedDate { get; set; }
    public bool? IsInvoiceReceived { get; set; }
    public DateTime? InvoiceReceivedAt { get; set; }
    public decimal? InvoiceAmount { get; set; }
    public bool? IsInvoiceApproved { get; set; }
    public DateTime? InvoiceApprovedAt { get; set; }
    public string InvoiceApprovedBy { get; set; }
    public bool? ChargeToTenant { get; set; }
    public bool? IsReportedToOwner { get; set; }
    public DateTime? ReportedToOwnerAt { get; set; }
    public int? InvoiceDocId { get; set; }
    public int? ReportedById { get; set; }
    public string ReportedBy { get; set; }
    public int? PriorityId { get; set; }
    public string Priority { get; set; }
    public int? QuotesRequired { get; set; }
    public bool? InvoiceOwner { get; set; }
    public bool? InvoiceTenant { get; set; }
    public bool? IsReportSentToTenant { get; set; }
    public DateTime? ReportSentToTenantAt { get; set; }
    public bool? IsJobCardSentToServiceProvider { get; set; }
    public int? JobCardSentToId { get; set; }
    public string JobCardSentTo { get; set; }
    public int? JobCardSentToServiceProviderById { get; set; }
    public string JobCardSentToServiceProviderBy { get; set; }
    public DateTime? JobCardSentToServiceProviderAt { get; set; }

    public int? StatusId { get; set; }
    public string Status { get; set; }
    public int? PropertyId { get; set; }
    public int? AssignedToId { get; set; }
    public string AssignedTo { get; set; }
    public string Comments { get; set; }
    public DateTime? AddedAt { get; set; }
    public DateTime? ModifiedAt { get; set; }
}
4

1 回答 1

1

我无法复制这种行为。我更新并使用了以下示例代码,其中包括GROUP_CONCAT您在评论中提到的聚合函数,它在 Pomelo 中没有问题5.0.0-alpha.2

using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using MySqlConnector;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;

namespace IssueConsoleTemplate
{
    public class IceCream
    {
        public int IceCreamId { get; set; }
        public string Name { get; set; }
        public string Company { get; set; }
        public string FullName { get; set; }
    }
    
    public class Context : DbContext
    {
        public virtual DbSet<IceCream> IceCreams { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var connectionString = "server=127.0.0.1;port=3306;user=root;password=;database=So65847111_GroupConcat";
            optionsBuilder.UseMySql(
                    connectionString,
                    ServerVersion.AutoDetect(connectionString),
                    options => options.CharSetBehavior(CharSetBehavior.NeverAppend))
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        configure => configure
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<IceCream>(
                entity =>
                {
                    entity.Property(e => e.Name)
                        .HasDefaultValue("Vanilla");
                    
                    entity.HasData(
                        new IceCream {IceCreamId = 1, Name = "Vanilla", Company = "Icecold"},
                        new IceCream {IceCreamId = 2, Name = "Chocolate", Company = "Mr IceCream"});
                });
        }
    }

    internal static class Program
    {
        private static void Main(string[] args)
        {
            using var context = new Context();
            
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();
            
            var iceCreamIdParameter = new MySqlParameter
            {
                ParameterName = "@iceCreamIdParam",
                MySqlDbType = MySqlDbType.Int32,
                Direction = System.Data.ParameterDirection.Input,
                Value = 42, // <-- does not exist
            };

            var iceCreams = context.IceCreams
                .FromSqlRaw(@"
SELECT `IceCreamId`, `Name`, `Company`, GROUP_CONCAT(`Name`, `Company` SEPARATOR ', ') AS `FullName`
FROM `IceCreams`
WHERE `IceCreamId` = @iceCreamIdParam
GROUP BY `IceCreamId`, `Name`, `Company`", iceCreamIdParameter)
                .AsNoTracking()
                .ToList();

            Trace.Assert(iceCreams.Count == 0);
        }
    }
}

因此,为了帮助您,我们需要更多信息:

  • 准确的 LINQ 查询(包括您在 FromSqlRaw() 调用中使用的准确 SQL 查询)
  • 数据库服务器类型(例如MySQLMariaDB)及其确切版本(例如8.0.21

(只需使用请求的信息更新 OP。)


异常提示,您返回的字段之一不包含它应该的类型(或可空性):

System.InvalidCastException:指定的强制转换无效。在 MySqlConnector.Core.Row.GetInt32(Int32 序数)

所以你的查询需要一个Int32,但返回的值是别的东西。让您的调试器在抛出异常时中断Row.GetInt32()并检查它尝试转换为的值Int32。然后检查ordinal方法的参数。ordinal最后,检查与EF Core 生成的 SQL 查询中的参数相同位置的表达式(您应该在堆栈Command.CommandText中对象的属性中找到它MySqlDataReader,或者您可以直接使用 EF Core 记录它)。


除此之外,您还可以运行我在您的服务器上发布的代码。它应该完美无缺。如果没有,那么这应该与一些意外的 MySQL 服务器设置有关。

于 2021-02-16T22:06:00.740 回答